【Python】单例模式实现方法详解
·
Python 单例模式实现方法详解
文章目录
1. 使用元类(Metaclass) - 推荐
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
class DatabaseConnection(metaclass=SingletonMeta):
def __init__(self):
print("初始化数据库连接")
# 测试
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True
代码解释:
- 定义元类 SingletonMeta:
- 元类是类的类,用于控制类的创建和行为。这里我们定义了一个元类 SingletonMeta,它继承自 type。
- 在元类中,我们定义了一个类变量
_instances,它是一个字典,用于存储已经创建过的类的实例。键是类,值是该类的唯一实例。- 在元类中重写
__call__方法:
- 当调用类(例如:DatabaseConnection())来创建实例时,会调用元类的
__call__方法。- 在
__call__方法中,我们检查当前类(cls)是否已经在_instances字典中。如果不在,说明还没有创建过该类的实例,那么我们就通过super().__call__(*args, **kwargs)来创建一个实例,并将其存入字典。如果已经存在,则直接返回字典中保存的实例。- 定义类 DatabaseConnection,并指定其元类为 SingletonMeta:
- 这样,当我们创建 DatabaseConnection 的实例时,会使用 SingletonMeta 的
__call__方法,从而确保每次返回的都是同一个实例。- 测试部分:
- 创建两个 DatabaseConnection 的实例 db1 和 db2。
- 打印
db1 is db2,检查两个变量是否指向同一个对象。由于单例模式,这里会输出 True。
创建实例过程:
DatabaseConnection()
↓
SingletonMeta.__call__() 被调用
↓
检查 cls._instances 字典
├─ 如果存在 → 返回已有实例
└─ 如果不存在 → 创建新实例并存储
2. 使用装饰器(Decorator)
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class ConfigManager:
def __init__(self):
self.settings = {}
print("配置管理器初始化")
# 测试
config1 = ConfigManager()
config2 = ConfigManager()
print(config1 is config2) # True
@singleton
class MyClass
↓
装饰器替换类引用
↓
MyClass 实际指向 get_instance 函数
↓
调用 MyClass() 时执行 get_instance()
↓
管理实例字典并返回单例
3. 重写 __new__ 方法
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
class Logger(Singleton):
def __init__(self):
if not hasattr(self, 'initialized'): # 防止重复初始化
self.logs = []
self.initialized = True
print("日志器初始化")
# 测试
logger1 = Logger()
logger2 = Logger()
print(logger1 is logger2) # True
4. 使用模块(Module) - 最简单
# singleton_module.py
class _Database:
def __init__(self):
print("数据库连接已创建")
database = _Database()
# main.py
from singleton_module import database
# 每次导入都是同一个实例
db1 = database
db2 = database
print(db1 is db2) # True
5. 使用类方法
class CacheManager:
_instance = None
def __init__(self):
self.cache = {}
print("缓存管理器初始化")
@classmethod
def get_instance(cls):
if not cls._instance:
cls._instance = cls()
return cls._instance
# 测试
cache1 = CacheManager.get_instance()
cache2 = CacheManager.get_instance()
print(cache1 is cache2) # True
6. 线程安全的单例模式
import threading
class ThreadSafeSingleton:
_instance = None
_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
with cls._lock:
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if not hasattr(self, 'initialized'):
self.data = {}
self.initialized = True
print("线程安全单例初始化")
# 测试
def create_singleton():
instance = ThreadSafeSingleton()
return instance
# 多线程测试
threads = []
for i in range(5):
t = threading.Thread(target=create_singleton)
threads.append(t)
t.start()
for t in threads:
t.join()
7. 使用 functools.lru_cache(Python 3.2+)[不推荐]
from functools import lru_cache
class Service:
def __init__(self, name):
self.name = name
print(f"服务 {name} 初始化")
@lru_cache(maxsize=None)
def get_service(service_name):
return Service(service_name)
# 测试
service1 = get_service("auth")
service2 = get_service("auth")
print(service1 is service2) # True
8. 继承时的注意事项
8.1元类和装饰器支持继承:
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
class BaseClass(metaclass=SingletonMeta):
pass
class ChildClass(BaseClass):
pass # ChildClass 有自己的单例实例
base1 = BaseClass() # BaseClass 实例
base2 = BaseClass() # 同一个 BaseClass 实例
child1 = ChildClass() # ChildClass 实例
child2 = ChildClass() # 同一个 ChildClass 实例
8.2 __new__ 方法继承问题:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
class ProblemChild(Singleton):
pass
parent = Singleton() # 创建 Singleton 实例
child = ProblemChild() # 返回同一个实例!因为 _instance 是类变量
9. 实际应用场景推荐
| 场景 | 推荐方法 | 理由 |
|---|---|---|
| Web应用配置管理 | 模块单例 | 简单,线程安全,导入即用 |
| 数据库连接池 | 线程安全元类 | 多线程环境,需要严格控制实例 |
| 日志记录器 | 装饰器 | 代码清晰,易于维护 |
| 缓存管理器 | 类方法 | 明确获取实例的意图 |
| 服务定位器 | 元类 | 管理多个服务单例 |
| 工具类 | __new__方法 | 简单直观 |
10. 性能考虑
- 模块单例: 性能最佳,Python模块天然单例
- 元类/装饰器: 轻微性能开销,但通常可忽略
- 线程安全版本: 加锁有性能开销,但确保安全
选择单例实现方法时,应根据项目规模、团队习惯、性能要求和线程安全需求来综合考虑。
更多推荐


所有评论(0)