__new__ 是单例实现的唯一入口,必须在此拦截重复创建并返回已有实例,__init__ 需加防护避免重复初始化,多线程需双重检查加锁,继承时应按类隔离实例,简单场景优先用模块级单例。

__new__ 是单例控制的唯一入口
单例的关键在于“只创建一次实例”,而 Python 中对象创建的实际控制权在 __new__,不是 __init__。__init__ 每次都会被调用(哪怕对象已存在),所以仅靠它无法实现单例——你改了状态,但实例已经多出了好几个。
必须在 __new__ 中拦截重复创建:检查类属性是否已有实例,有就直接返回,没有才调用父类 super().__new__(cls) 创建新实例。
-
__new__必须返回一个实例(通常是cls类型),否则__init__根本不会触发 - 不能在
__new__里调用self.xxx = yyy(此时self还没诞生),初始化逻辑仍应留在__init__ - 类变量(如
_instance)要定义在类体顶层,不能放在__new__内部,否则每次调用都重置
__init__ 只负责首次初始化,需加防护
即使 __new__ 正确返回了已有实例,Python 仍会无条件调用 __init__。如果不加判断,每次 Singleton() 都会重置状态,单例就“形同虚设”了。
常见做法是在实例上打标记(比如设置 self._initialized = True),或检查某个关键属性是否已存在:
def __init__(self):
if hasattr(self, '_initialized') and self._initialized:
return
# 初始化逻辑...
self._initialized = True
- 不要用
if self.__class__._instance is not None:判断——_instance是类属性,但self是实例,这种写法易混淆且不直观 - 避免在
__init__里做耗时操作(如连接数据库),因为可能被反复执行;真正需要“只做一次”的逻辑,必须由__new__或带防护的__init__承担
线程安全不能靠直觉,得显式加锁
多线程环境下,两个线程可能同时卡在 if cls._instance is None: 判断之后、赋值之前,导致创建两个实例。标准库 threading.Lock 是最稳妥的选择:
import threading
<p>class Singleton:
_instance = None
_lock = threading.Lock()</p><pre class="brush:python;toolbar:false;">def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
- 双重检查(double-checked locking)必须两次判空,外层减少锁竞争,内层防止重复创建
- 不要用
@staticmethod或函数级锁——锁对象必须绑定到类,否则不同子类会共用同一把锁,引发意外覆盖 - 如果项目用了 asyncio,
threading.Lock不适用;需换用asyncio.Lock,且整个构造流程得异步化(__new__本身不能是 async,得另寻方案)
继承与元类会让单例行为变复杂
直接继承单例类时,子类默认会共享父类的 _instance,但多数场景下这是错的——Child() 应该返回 Child 实例,而不是 Parent 实例。
解决方法是让 _instance 按类隔离:
def __new__(cls):
if not hasattr(cls, '_instances'):
cls._instances = {}
if cls not in cls._instances:
cls._instances[cls] = super().__new__(cls)
return cls._instances[cls]
- 元类(
metaclass)方案更彻底,但调试难度高;90% 的需求用带类隔离的__new__就够了 - 如果用了依赖注入框架(如 dependency-injector 或 pytest 的 fixture),别自己手写单例——框架通常已有更健壮的生命周期管理
- Python 的模块本身就是天然单例,简单场景(如配置类)直接用模块级变量比类单例更清晰、更不易出错
单例最难的部分从来不是写那几行代码,而是想清楚“到底要对哪个粒度保证唯一性”——是整个进程、某个线程、还是某次请求?选错粒度,锁再细也没用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











