Python单例模式:5种高效实现方式_python 单例
单例模式是一种设计模式,确保一个类只有一个实例,并提供全局访问点。以下用 Python 实现单例模式的几种方式(装饰器、元类等)。
1. 使用装饰器实现单例模式
装饰器是一个函数,用来包装另一个函数或类,修改其行为。我们可以用装饰器来确保类只创建一个实例。
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 SingletonClass: def __init__(self, name): self.name = name# 测试a = SingletonClass(\"Instance A\")b = SingletonClass(\"Instance B\")print(a is b) # True,说明是同一个实例print(a.name, b.name) # Instance A Instance A
通俗解释:
- 装饰器就像一个“门卫”,每次你想创建新实例时,它先检查有没有现成的实例。
instances
是一个字典,键是类,值是实例。如果类没在字典里,就创建一个实例放进去;如果已经有了,就直接返回那个实例。@singleton
让类每次被调用时都通过装饰器检查,确保返回同一个对象。- 这里
a
和b
是同一个实例,所以b
的创建不会改变name
,仍然是Instance A
。
2. 使用元类实现单例模式
元类是“类的类”,可以控制类的创建过程。我们可以通过元类来实现单例模式。
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 SingletonClass(metaclass=SingletonMeta): def __init__(self, name): self.name = name# 测试a = SingletonClass(\"Instance A\")b = SingletonClass(\"Instance B\")print(a is b) # True,说明是同一个实例print(a.name, b.name) # Instance A Instance A
通俗解释:
- 元类就像一个“模具”,决定了类如何被创建。
SingletonMeta
是一个自定义元类,控制SingletonClass
的实例化过程。 - 当你尝试创建
SingletonClass
的实例时,元类的__call__
方法会检查_instances
字典。 - 如果类已经有实例,就直接返回;否则,调用父类的
__call__
创建新实例并存起来。 - 效果和装饰器类似,但元类是更底层的机制,直接干预类的创建。
3. 使用类属性实现单例模式(简单方式)
这是最简单的一种实现方式,直接在类内部维护一个实例。
class SingletonClass: _instance = None # 存储唯一实例 def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls) # 创建新实例 return cls._instance def __init__(self, name): if not hasattr(self, \'initialized\'): # 防止重复初始化 self.name = name self.initialized = True# 测试a = SingletonClass(\"Instance A\")b = SingletonClass(\"Instance B\")print(a is b) # True,说明是同一个实例print(a.name, b.name) # Instance A Instance A
通俗解释:
__new__
是 Python 中创建对象的方法,在__init__
之前执行。我们在__new__
中检查_instance
是否存在。- 如果
_instance
是None
,就创建一个新实例;否则直接返回已有实例。 __init__
可能会被多次调用(每次创建对象时),所以用initialized
标志防止重复初始化name
。- 这种方式简单直接,适合小型项目,但不如元类或装饰器灵活。
4. 使用模块级单例(最简单方式)
Python 的模块本身就是单例的,因为模块只加载一次。我们可以直接利用这一点。
# singleton.pyclass SingletonClass: def __init__(self, name): self.name = namesingleton_instance = SingletonClass(\"Module Singleton\")# 测试 (在另一个文件中)# from singleton import singleton_instance# print(singleton_instance.name) # Module Singleton
通俗解释:
- Python 模块加载时只执行一次,
singleton_instance
在模块加载时创建,之后全局唯一。 - 这不是严格的单例模式(没有控制类的实例化),但在实际开发中常用来实现全局唯一对象。
- 优点是简单,缺点是不够灵活,适合静态配置场景。
5. 线程安全的单例模式(装饰器改进)
在多线程环境中,多个线程可能同时创建实例,导致单例失效。我们可以用锁来保证线程安全。
from threading import Lockdef singleton(cls): instances = {} lock = Lock() # 创建线程锁 def get_instance(*args, **kwargs): with lock: # 确保线程安全 if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance@singletonclass SingletonClass: def __init__(self, name): self.name = name# 测试import threadingdef test_singleton(): inst = SingletonClass(\"Test\") print(inst.name)threads = [threading.Thread(target=test_singleton) for _ in range(10)]for t in threads: t.start()for t in threads: t.join()
通俗解释:
- 多线程环境下,两个线程可能同时检查
instances[cls]
,导致创建多个实例。 Lock
就像一个“排队机制”,确保同一时间只有一个线程能创建实例。- 使用
with lock
保证线程安全,其他线程必须等待锁释放。
比较与选择
- 装饰器:代码简洁,易于理解,适合大多数场景。线程安全的装饰器适合多线程环境。
- 元类:更底层,适合需要深度定制类的场景,但代码稍复杂。
- 类属性:最简单,适合小型项目,但需要注意
__init__
重复调用问题。 - 模块级单例:最简单但不灵活,适合全局配置对象。
- 线程安全:在多线程场景下必须考虑,使用锁是常见解决方案。
总结
- 单例模式的核心是“全局唯一实例”,可以通过装饰器、元类、类属性等方式实现。
- 装饰器和元类是最常见的实现方式,元类更灵活但更复杂。
- 多线程场景需要加锁保证线程安全。
- 如果只需要一个全局对象,模块级单例是最简单的选择。