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

代码解释:

  1. 定义元类 SingletonMeta:
    • 元类是类的类,用于控制类的创建和行为。这里我们定义了一个元类 SingletonMeta,它继承自 type。
    • 在元类中,我们定义了一个类变量 _instances,它是一个字典,用于存储已经创建过的类的实例。键是类,值是该类的唯一实例。
  2. 在元类中重写 __call__ 方法:
    • 当调用类(例如:DatabaseConnection())来创建实例时,会调用元类的 __call__ 方法。
    • __call__ 方法中,我们检查当前类(cls)是否已经在 _instances 字典中。如果不在,说明还没有创建过该类的实例,那么我们就通过 super().__call__(*args, **kwargs) 来创建一个实例,并将其存入字典。如果已经存在,则直接返回字典中保存的实例。
  3. 定义类 DatabaseConnection,并指定其元类为 SingletonMeta:
    • 这样,当我们创建 DatabaseConnection 的实例时,会使用 SingletonMeta 的 __call__ 方法,从而确保每次返回的都是同一个实例。
  4. 测试部分:
    • 创建两个 DatabaseConnection 的实例 db1 和 db2。
    • 打印 db1 is db2,检查两个变量是否指向同一个对象。由于单例模式,这里会输出 True。
Client DatabaseConnection SingletonMeta db1 = DatabaseConnection() 因为DatabaseConnection的元类是SingletonMeta, 所以实例化时调用SingletonMeta的__call__ __call__(DatabaseConnection) 检查DatabaseConnection是否在_instances中 不在,则创建新实例 super().__call__() 即调用type的__call__,创建实例 __init__() 返回实例db1 将DatabaseConnection和db1的映射存入_instances db2 = DatabaseConnection() __call__(DatabaseConnection) 检查DatabaseConnection在_instances中 返回已存在的实例db1(即db2也指向db1) Client DatabaseConnection SingletonMeta
创建实例过程:
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
Client SingletonClass object __new__(cls) 检查 cls._instance super().__new__(cls) 新实例 cls._instance = 新实例 返回 _instance alt [_instance 为 None] [_instance 已存在] __init__(self) 返回实例 Client SingletonClass object

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模块天然单例
  • 元类/装饰器: 轻微性能开销,但通常可忽略
  • 线程安全版本: 加锁有性能开销,但确保安全

选择单例实现方法时,应根据项目规模、团队习惯、性能要求和线程安全需求来综合考虑。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐