Python 单例模式

Python 中的单例模式(Singleton Pattern)

单例模式是一种创建型设计模式,其核心目的是:确保一个类全局只有一个实例,并提供一个全局访问点来获取该实例

常见应用场景:

  • 数据库连接池
  • 日志器(Logger)
  • 配置管理器(Config Manager)
  • 线程池
  • 缓存对象

单例模式的优点

  • 节省资源(只创建一次)
  • 全局统一访问
  • 控制实例数量

缺点

  • 难以单元测试(全局状态)
  • 可能隐藏依赖关系
  • 在多线程环境下需小心实现

Python 中实现单例模式的几种常见方式

Python 是动态语言,实现单例非常灵活。下面从简单到推荐依次介绍。

1. 使用模块级变量(最 Pythonic,最推荐!)

Python 模块本身就是天然的单例,所有导入模块的代码共享同一个实例。

# singleton.py
class _Singleton:
    def __init__(self):
        print("Singleton created")

    def do_something(self):
        print("Doing something...")

instance = _Singleton()  # 模块级实例,只创建一次

# 在其他文件中使用
# from singleton import instance

from singleton import instance

print(instance)  # 同一个对象
instance.do_something()

优点:简单、无需额外代码、线程安全、天生单例
这是大多数 Python 项目中实际使用的“单例”方式

2. 使用 __new__ 方法重写(经典方式)
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        # 注意:__init__ 可能会被调用多次!
        # 如果需要初始化逻辑,要防止重复执行
        if not hasattr(self, '_initialized'):
            print("Initializing singleton...")
            self._initialized = True

# 测试
s1 = Singleton()
s2 = Singleton()
print(s1 is s2)  # True
print(id(s1), id(s2))  # 相同地址

注意__init__ 会被调用多次(每次实例化时),所以初始化逻辑要加保护。

3. 使用装饰器实现
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 MyClass:
    def __init__(self, value):
        self.value = value

# 使用
a = MyClass(10)
b = MyClass(20)
print(a is b)      # True
print(a.value)     # 10(第一次传入的值生效,后续忽略)
4. 使用元类(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("Connecting to database...")

# 使用
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2)  # True
5. 线程安全的单例(使用锁)

如果在多线程环境中使用 __new__ 方式,建议加锁避免竞争:

import threading

class ThreadSafeSingleton:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            with cls._lock:
                # 双重检查(Double-Checked Locking)
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance

推荐总结

方式推荐度线程安全简洁性说明
模块导入★★★★★最高最 Pythonic,强烈推荐
__new__ 重写★★★★需加锁经典方式
装饰器★★★灵活但参数会被忽略
元类★★强大但复杂
threading.Lock★★★多线程必备

实际建议

  • 99% 的场景下,使用模块级单例就够了,不要过度设计。
  • 如果你真的需要一个“类”的单例行为,使用 __new__ 或元类。
  • 避免在单例中持有可变全局状态(容易导致 bug)。

如果你想看单例在实际项目中的应用(如日志器、配置中心),或者结合其他模式(如工厂+单例),可以告诉我!

文章已创建 3511

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部