Python 中如何实现单例模式?

Python 实现单例的几种方式:模块级实例、__new__ 方法、装饰器和元类。本文对比各种实现,重点介绍可复用的元类写法。

最佳实践
Python 中如何实现单例模式?封面

单例模式保证一个类全局只有一个实例。Python 里最"Pythonic"的方式其实是不写模式——直接用模块级对象;需要类形态时,元类(metaclass)方案最通用:控制 __call__,让每次实例化都返回同一个对象。

方法一:元类(可复用,推荐)

class Singleton(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 Logger(metaclass=Singleton):
    pass

a = Logger()
b = Logger()
print(a is b)  # True

实例化类时实际调用的是元类的 __call__,在这里拦截并缓存实例,任何类加上 metaclass=Singleton 就变成单例。

方法二:重写 new

class Logger:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

简单直接,但注意 __init__ 每次实例化都会执行,若初始化有副作用需要加标志位防护。

方法三:模块级单例(最 Pythonic)

# logger.py
class Logger:
    ...

logger = Logger()   # 模块只被导入一次,这个对象天然是单例

使用时 from logger import logger。Python 模块机制保证了导入缓存,绝大多数场景这就够了。

常见问题(FAQ)

Q:多线程下这些实现安全吗?
A:以上写法都不是线程安全的(两个线程可能同时通过 if 检查)。元类方案可以加一把 threading.Lock 保护 _instances 检查与创建过程。

Q:单例有什么缺点?
A:本质是全局状态,会让单元测试互相污染、隐藏依赖关系。使用前考虑是否真的需要——依赖注入通常是更好的设计。

Q:装饰器方式怎么写?
A:把类的实例化包装一层:用一个字典缓存 cls -> instance,每次调用装饰后的类时先查缓存,效果和元类类似但不改变类的继承结构。

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台