> 技术文档 > Python单例模式:5种高效实现方式_python 单例

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 让类每次被调用时都通过装饰器检查,确保返回同一个对象。
  • 这里 ab 是同一个实例,所以 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 是否存在。
  • 如果 _instanceNone,就创建一个新实例;否则直接返回已有实例。
  • __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__ 重复调用问题。
  • 模块级单例:最简单但不灵活,适合全局配置对象。
  • 线程安全:在多线程场景下必须考虑,使用锁是常见解决方案。

总结

  • 单例模式的核心是“全局唯一实例”,可以通过装饰器、元类、类属性等方式实现。
  • 装饰器和元类是最常见的实现方式,元类更灵活但更复杂。
  • 多线程场景需要加锁保证线程安全。
  • 如果只需要一个全局对象,模块级单例是最简单的选择。