__new__更适合控制单例创建,因为它是对象内存分配阶段的入口,可拦截重复实例化;而__init__仅负责初始化,每次调用都会执行,无法阻止多余对象创建。

为什么 __new__ 比 __init__ 更适合控制单例创建
因为 __init__ 每次实例化都会被调用,而单例的核心是“只生成一次实例”,必须在对象分配内存阶段就拦截重复创建。Python 中真正负责构造实例的是 __new__——它返回一个新对象,__init__ 只负责初始化。如果在 __new__ 里缓存并复用已有实例,就能跳过后续构造流程。
常见错误是只重写 __init__ 并加标志位,结果每次调用仍会新建对象再覆盖属性,内存和初始化开销照旧。
-
__new__必须返回一个类的实例(通常是super().__new__(cls)),否则__init__不会被调用 - 若已存在实例,直接返回缓存对象,不要再次调用
super().__new__(cls) - 注意线程安全:多线程下
__new__可能并发进入,需加锁或使用模块级变量+判断
用元类实现线程安全的单例:为什么推荐 type.__call__
元类通过重写 __call__ 控制类的调用行为(即 MyClass() 这一动作),比在每个类里手动写 __new__ 更统一、更易复用。关键是把实例缓存放在元类内部,而非类本身——避免子类意外覆盖或继承混乱。
典型陷阱是把缓存字典定义在元类的 __init__ 里,导致每个元类实例(即每个使用该元类的类)都有独立缓存,失去“全局唯一”意义。正确做法是将缓存设为元类的类属性。
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 Database(metaclass=SingletonMeta):
def __init__(self):
self.connection = "active"
- 子类自动继承单例行为,无需额外声明
- 不同类(如
Database和Cache)各自有独立实例,互不干扰 - 若需真正全局唯一(所有类共用一个实例),需另行设计,但违背单例本意
装饰器实现单例时,为什么不能直接装饰类定义
装饰器本质是函数,接收并返回一个可调用对象。当你写 @singleton 在 class A: 上方时,装饰器实际接收的是类对象,返回的也应是类对象——但很多初学者误把它当成“给类加方法”,试图在装饰器里修改 __new__ 却不返回新类,导致语法错误或静默失效。
更隐蔽的问题是装饰器闭包捕获的实例变量,在 reload 模块或多次导入时可能残留旧引用,造成“伪单例”。
- 装饰器必须返回一个新类(通常用
type()动态构造,或返回包装类),不能原地修改原类 - 缓存应放在装饰器函数外部作用域(如闭包变量),且需考虑模块生命周期
- 相比元类,装饰器对继承支持较弱:子类不会自动获得单例特性,除非显式再装饰
哪种方式更适合你的项目:元类 vs 装饰器 vs 模块级实例
元类适合框架层统一管控,比如 ORM 或配置管理器,要求所有子类天然单例;装饰器适合局部、明确的单例需求,比如某个工具类只需在一处生效;而最简单可靠的方案其实是“模块级单例”——直接在模块中实例化一次并导出,Python 的模块导入机制天然保证唯一性。
很多人忽略这点,非要用复杂机制实现,结果引入线程竞争、热重载异常、测试 mock 困难等问题。
- 模块级单例:
db.py中写instance = Database(),其他地方from db import instance - 元类适合需要运行时动态决定是否单例的场景(如根据配置开关)
- 装饰器若用于单元测试,需支持临时解除单例(例如传参
@singleton(override=True))
真正难的不是写出三种实现,而是判断哪一种在当前上下文里副作用最小、最易调试、最不容易被未来同事误解。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











