一、单例模式核心思想
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。该模式主要解决以下问题:
- 资源控制(如数据库连接池)
- 配置信息全局一致性
- 避免重复创建消耗资源的对象
二、Python实现单例的5种方式
1. 模块级单例(Pythonic方式)
# singleton.py
class _Singleton:
def __init__(self):
self.value = None
instance = _Singleton()
# 使用方式
from singleton import instance
instance.value = "Hello"
原理:Python模块在首次导入时会执行初始化,后续导入都使用同一个实例
2. 装饰器实现
def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class Logger:
pass
优势:可复用装饰器,不影响类原有逻辑
3. __new__方法重写
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
注意:需同时重写__init__
避免重复初始化
4. 元类实现
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
pass
特点:在类创建层面控制实例化行为
5. 线程安全单例(带锁版)
from threading import Lock
class ThreadSafeSingleton:
_instance = None
_lock = Lock()
def __new__(cls):
if not cls._instance:
with cls._lock:
if not cls._instance: # 双重检查
cls._instance = super().__new__(cls)
return cls._instance
三、单例模式深度分析
生命周期管理
- Python的单例生命周期与模块/程序相同
- 可通过弱引用(weakref)实现可销毁的单例
继承问题
子类化单例类时需注意:
class BaseSingleton(metaclass=SingletonMeta): pass
class ChildA(BaseSingleton): pass # 正确
class ChildB(BaseSingleton): pass # 与ChildA不是同一个实例
测试注意事项
- 使用
unittest.mock.patch
替换单例实例 - 测试间注意清理单例状态
四、实际应用场景
典型案例
- 日志记录器(全局统一日志配置)
- 数据库连接池管理
- 应用配置管理
- 设备驱动访问(如打印机)
Django中的单例实践
# settings.py 本质是模块级单例
from django.conf import settings
# middleware.py 中间件通常设计为单例
class CustomMiddleware:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.init_resources()
return cls._instance
五、反模式与替代方案
滥用单例的坏处
- 隐式耦合(hidden dependencies)
- 测试困难
- 违反单一职责原则
替代方案
- 依赖注入(Dependency Injection)
- 全局变量(简单场景)
- 上下文管理器(资源管理场景)
六、性能优化建议
- 使用
__slots__
减少内存占用 - 延迟初始化(Lazy Initialization)
- 考虑使用functools.lru_cache实现缓存式单例
from functools import lru_cache
@lru_cache(maxsize=1)
def get_config():
return load_config_file()
七、总结
Python实现单例的推荐选择:
- 简单场景:模块变量
- 需要继承:装饰器或元类
- 多线程环境:带锁实现
- 需要缓存功能:lru_cache
最佳实践:除非确需全局唯一实例,否则优先考虑依赖注入等更灵活的模式