概览
__new__(cls, *args, **kwargs)—— 创建新实例(分配内存并返回实例对象)。通常返回super().__new__(cls)的结果;对不可变类型(int/str/tuple)或需要控制实例创建时的场景经常重写它。__init__(self, *args, **kwargs)—— 对新创建的实例做初始化(设置属性等),但不负责分配内存。只有当__new__返回的是cls(或其子类)的实例时,__init__才会被调用。__del__(self)—— 在对象即将被销毁时调用(终结器 / finalizer)。调用时机不确定(特别是在不同 Python 实现或解释器关闭时),且有许多陷阱——通常推荐使用上下文管理或weakref.finalize取代它。
对象生命周期
-
Python 调用类(
C())时,实际执行type.__call__(即类对象的__call__)。 -
type.__call__内部先调用cls.__new__(cls, *args, **kwargs)获得实例obj。 -
如果
obj是cls的实例(isinstance(obj, cls)为真),则调用cls.__init__(obj, *args, **kwargs)初始化该实例。- 如果
__new__返回不是cls的实例,则不会调用__init__。
- 如果
-
当对象变为不可达,垃圾回收触发析构时,若满足条件(参考计数为 0 或 GC 回收)则调用
obj.__del__()(但并不保证在所有实现或 shutdown 阶段一定调用)。
__new__ 详解(创建阶段——控制实例的产生)
作用与签名
def __new__(cls, *args, **kwargs) -> object:
...
- 是一个类方法(第一个参数是类
cls),用于创建并返回一个新对象(通常是cls的实例)。 - 必须返回一个对象;若返回
None或非对象类型会导致错误。 - 常见的返回是
super().__new__(cls)(内建object.__new__)或某个已有实例(用于单例、对象池等)。
什么时候重写 __new__
- 你要创建不可变类型的子类(
int/str/tuple):因为__init__在对象创建后运行,无法改变对象本身的内在状态,必须在__new__中设置值。 - 实现 单例/实例复用/对象池:
__new__可以返回已有实例,避免重复创建。 - 需要在创建时干预(例如从缓存中取对象、记录分配、或给底层 C 扩展传参)。
重要语义与边界
-
若
__new__返回cls的实例,则 Python 会接着调用__init__(对返回的那个实例)。 -
若
__new__返回非cls的对象(比如返回一个int、或另一个类的实例),则 不会 调用__init__。这可用于工厂样式:A()可能返回B()的实例。 -
返回子类实例(
isinstance(obj, cls)为真)→__init__仍会被调用,但调用的是返回对象类型的__init__(查找在对象类型上)。 -
典型写法(最简单安全写法):
def __new__(cls, *args, **kwargs): self = super().__new__(cls) # 可能做些原始字段分配/检查 return self
示例:打印创建与初始化顺序
class A:
def __new__(cls, *args, **kwargs):
print("__new__", cls, args, kwargs)
return super().__new__(cls)
def __init__(self, x):
print("__init__", self, x)
def __repr__(self):
return f"<A id={id(self)}>"
a = A(10)
# 输出:
# __new__ <class '__main__.A'> (10,) {}
# __init__ <A id=...> 10
示例:不可变类型(以 str 为例)
class UpperStr(str):
def __new__(cls, content):
# 在 __new__ 中转成大写 —— 因为 str 是不可变的
return str.__new__(cls, content.upper())
def __init__(self, content):
# __init__ 仍会被调用,但不能再改变字符串的值
print("init called with", content)
s = UpperStr("hello")
print(s) # HELLO
示例:单例(并示范 init 重入问题)
class Singleton:
_inst = None
def __new__(cls, *args, **kwargs):
if cls._inst is None:
cls._inst = super().__new__(cls)
return cls._inst
def __init__(self, value=None):
# __init__ 会在每次调用时被调用(因为返回的对象是 cls 的实例)
# 常见做法是用已初始化标志避免重复初始化:
if hasattr(self, "_initialized"):
return
self.value = value
self._initialized = True
a = Singleton(1)
b = Singleton(2)
assert a is b
assert a.value == 1 # 由于 _initialized,第二次 __init__ 不会覆盖
返回不同类型 -> 跳过 __init__
class Factory:
def __new__(cls, flag):
if flag:
return super().__new__(cls)
else:
return 123 # 返回非实例 -> __init__ 不会被调用
f = Factory(False) # 返回 123
# 注意:这里 f 不是 Factory 的实例
__init__ 详解(初始化阶段——配置实例状态)
作用与签名
def __init__(self, *args, **kwargs) -> None:
...
- 不是构造器(不负责分配内存),是初始化器:在
__new__返回合格实例后被调用。 - 可以设置属性、校验参数、打开资源(不推荐在此直接打开关键系统资源以避免析构问题,但可以根据需要做一些初始化)。
- 若
__init__抛异常,则实例创建被视为失败(异常传播)——此时对象可能会被销毁。
注意点
- 在多重继承/协作式继承中,应使用
super().__init__(),以确保所有基类都能以 cooperative 的方式初始化。 __init__可以被多次调用(比如单例返回同一实例、但__init__每次都被调用),因此如果不想重初始化,需做已初始化标志判断。__init__的返回值必须是None(返回其他值是无意义的,会被忽略,但不应该返回非 None)。
示例:协作式初始化(多继承)
class Base:
def __init__(self):
print("Base init")
class Mixin:
def __init__(self):
print("Mixin init")
super().__init__()
class Child(Mixin, Base):
def __init__(self):
print("Child init")
super().__init__()
Child()
# 输出(由 MRO 决定):
# Child init
# Mixin init
# Base init
__del__ 详解(析构 / finalizer)
作用与签名
def __del__(self):
...
- 在对象即将被销毁时 Python 会尝试调用它(终结器),常用于释放外部资源(文件句柄、套接字、临时文件等),但其调用时机与保证性都有限。
- 在不同 Python 实现(CPython / PyPy / Jython)与不同情形(引用计数为 0 / GC 清理 / 解释器退出)下,
__del__的调用行为不同,所以不应依赖它做关键资源释放。
关键陷阱 & 注意事项
- 不确定性:在 PyPy、JVM(Jython)等实现中,GC 是非确定性的,
__del__可能延迟很久甚至不会及时执行。 - 解释器退出时:在程序退出阶段(interpreter shutdown),模块全局变量可能已被置为
None,__del__中访问这些全局会失败或抛异常;因此在__del__中避免引用模块级资源,或在__del__中进行存在性检查。 - 循环引用:如果对象参与引用循环,并且实现了
__del__,CPython 的垃圾回收器会将这种循环中的对象放入gc.garbage而不会自动调用__del__,因为确定执行顺序会不安全。 - 异常处理:
__del__中的异常不会传播(会被打印到sys.stderr),但这也可能掩盖问题。 - 复活对象(Resurrection):在
__del__中把self再次绑定到全局变量或其他地方可以“复活对象”,这种行为非常危险且难以预测(不推荐)。 - 若想确保资源释放,优先用
with/ context manager 或weakref.finalize。
示例:__del__ 与循环的危险
import gc
class Node:
def __init__(self):
self.ref = self # 自引用,形成循环
def __del__(self):
print("Node.__del__ called")
n = Node()
del n
# 对于通常 CPython,可能不会立即打印 __del__,
# gc 会检测循环,但因为有 __del__,不会自动回收该循环。
gc.collect()
print("garbage:", gc.garbage)
- 上面带
__del__的循环对象往往不会被安全销毁而是放到gc.garbage,你需要手动断开循环或移除__del__。
推荐替代:context manager 与 weakref.finalize
使用 with(首选)
class Resource:
def __enter__(self):
self.f = open("tmp.txt","w")
return self.f
def __exit__(self, exc_type, exc, tb):
self.f.close()
with Resource() as f:
f.write("hello")
# 确保及时关闭
使用 weakref.finalize(适合对象超出作用域时回收资源)
import weakref, tempfile
class Resource:
def __init__(self, name):
self._f = open(name, "w")
# 注册 finalizer:当 Resource 实例被回收时,确保文件被关闭。
self._finalizer = weakref.finalize(self, Resource._cleanup, self._f)
@staticmethod
def _cleanup(f):
try:
f.close()
except Exception:
pass
r = Resource("t.txt")
del r
# 当 r 被回收时 _cleanup 会被调用,且不依赖 __del__ 的复杂规则
weakref.finalize 的好处:不会因对象处于循环引用中而丢失回调,行为更可靠;也能避免 del 在解释器关闭阶段的问题。
__del__ 在解释器关闭阶段的典型问题
- 在解释器退出时,模块级名可能已被设置为
None,若__del__依赖模块级变量(例如 logger),则可能抛异常或不能工作。若确实需要在__del__中使用模块对象,先检查是否为None。
与 pickling / 拷贝 等的交互(补充重要点)
- Pickle 与
__new__:当对象被 unpickle 时,__new__会被用来创建实例(通过__getnewargs__/__getnewargs_ex__可以给__new__提供参数),之后再通过__setstate__或直接设置__dict__恢复状态。通常__init__不会被再次调用(取决于 pickling 协议)。 - 复制:
copy.copy会调用对象的__reduce__/__reduce_ex__或利用__getstate__/__setstate__;__init__不一定被调用。要在复制时控制行为,可以定义__copy__/__deepcopy__。
常见用例与示范代码集合
1. 打印生命周期演示
class Demo:
def __new__(cls, *a, **k):
print("__new__", cls, a, k)
return super().__new__(cls)
def __init__(self, x):
print("__init__", self, x)
def __del__(self):
print("__del__", self)
d = Demo(5)
del d
2. 不可变类型(tuple)的定制
class Point(tuple):
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
def x(self): return self[0]
def y(self): return self[1]
3. 对象池(示例:复用实例)
class Pooled:
_pool = []
def __new__(cls, *args, **kwargs):
if cls._pool:
obj = cls._pool.pop()
print("reuse", obj)
return obj
return super().__new__(cls)
def __init__(self, value):
self.value = value
def release(self):
# 手动返回池中
self._pool.append(self)
p1 = Pooled(1)
p1.release()
p2 = Pooled(2) # 可能 reuse p1; __init__ 被再次调用 -> 注意需处理重初始化
4. 单例(推荐简单可靠的模块单例,而非复杂 new 单例)
# module-level singletons are simplest:
# my_singleton.py
class Service:
pass
service = Service()
# import my_singleton; my_singleton.service 即单例
常见问题答疑(FAQ)
Q1:如果 __new__ 返回已有实例,那么 __init__ 会被调用吗?
A:如果返回的对象是 isinstance(obj, cls) 为真(即返回对象属于 cls 或其子类),Python 会调用 __init__(对该对象);因此在单例/池化实现中要注意 __init__ 会重复运行,通常通过检查 _initialized 标志或在 __new__ 中避免再次初始化来处理。
Q2:__del__ 能保证在程序退出时调用吗?
A:不能保证——解释器关闭时对象引用可能被置为 None,不同实现也差异很大。不要把关键释放逻辑放在 __del__ 中;使用 with 或 weakref.finalize。
Q3:如何避免 __del__ 与循环引用的冲突?
A:避免在对象中写 __del__;改用 weakref.finalize。或者确保在清理前手动断开循环引用(例如把大结构的引用置为 None)。
Q4:__new__、__init__、__del__ 的相对性能影响?
A:__new__ 与 __init__ 的额外开销通常微小;但在高性能场景(比如每帧创建大量对象)下,避免频繁创建/销毁对象(可使用对象池、复用或 C 扩展)更有意义。__del__ 影响 GC(与循环引用交互),不适合用于大量对象。
实践总结
- 优先使用
__init__来初始化实例状态;仅在需要控制实例创建(不可变类型、缓存/池、单例)时覆盖__new__。 - 在多继承中统一使用
super(),并让每个类的__init__/__new__支持 cooperative 调用。 - 避免在
__init__/__new__中执行过多耗时任务(比如 I/O);那类工作宜在显式方法中做或移到后台线程。 - 不要依赖
__del__释放关键系统资源。对于显式生命周期使用with(上下文管理器)或weakref.finalize注册清理。 - 如果要实现单例或对象缓存,显式处理重复初始化(检查
_initialized标志或用工厂函数/模块单例替代)。 - 对于不可变类型(
str/tuple/int)的子类,在__new__中设置值,不要期待__init__可以修改对象内容。
示例
示例 1:__new__ 与 __init__ 执行顺序
# file: test_new_init.py
import unittest
class Demo:
def __new__(cls, *args, **kwargs):
print(f"__new__ called, cls={cls}, args={args}, kwargs={kwargs}")
# 必须调用父类 __new__ 才能真正创建对象
instance = super().__new__(cls)
return instance
def __init__(self, value):
print(f"__init__ called, self={self}, value={value}")
self.value = value
class TestNewInit(unittest.TestCase):
def test_new_and_init(self):
d = Demo(42)
self.assertEqual(d.value, 42)
if __name__ == "__main__":
unittest.main()
运行结果(简化):
__new__ called, cls=<class '__main__.Demo'>, args=(42,), kwargs={}
__init__ called, self=<__main__.Demo object>, value=42
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
可以看到 先调用 __new__,再调用 __init__。
示例 2:自定义单例模式(依赖 __new__)
# file: test_singleton.py
import unittest
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
print("Creating new instance")
cls._instance = super().__new__(cls)
else:
print("Reusing existing instance")
return cls._instance
def __init__(self, value):
# 注意:即使是重复获取单例,也会调用 __init__!
print(f"__init__ called with value={value}")
self.value = value
class TestSingleton(unittest.TestCase):
def test_singleton_identity(self):
a = Singleton(1)
b = Singleton(2)
self.assertIs(a, b)
# 因为 __init__ 每次都执行,最后 value=2
self.assertEqual(a.value, 2)
if __name__ == "__main__":
unittest.main()
演示 __new__ 控制实例唯一性,而 __init__ 每次都会执行。
示例 3:__del__ 与垃圾回收
# file: test_del_gc.py
import unittest
import gc
import weakref
class Tracked:
def __init__(self, name):
self.name = name
print(f"[__init__] Created {self.name}")
def __del__(self):
print(f"[__del__] Destroying {self.name}")
class TestDelGC(unittest.TestCase):
def test_del_behavior(self):
obj = Tracked("obj1")
ref = weakref.ref(obj)
self.assertIsNotNone(ref())
del obj # 删除变量名,但对象可能仍在
# 强制触发垃圾回收
gc.collect()
self.assertIsNone(ref())
if __name__ == "__main__":
unittest.main()
运行时,你会看到:
[__init__] Created obj1
[__del__] Destroying obj1
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
__del__ 在对象被销毁时调用,但调用时机 不确定,只有在引用计数归零且 GC 回收时才触发。
示例 4:循环引用 + gc
# file: test_cycle_gc.py
import unittest
import gc
class Node:
def __init__(self, name):
self.name = name
self.ref = None
print(f"Node {self.name} created")
def __del__(self):
print(f"Node {self.name} destroyed")
class TestCycleGC(unittest.TestCase):
def test_cycle(self):
a = Node("A")
b = Node("B")
a.ref = b
b.ref = a # 构成循环引用
del a, b
print("Deleted references, running gc.collect() ...")
unreachable = gc.collect()
print(f"GC collected {unreachable} objects")
if __name__ == "__main__":
unittest.main()
运行结果(可能因环境不同而不同):
Node A created
Node B created
Deleted references, running gc.collect() ...
Node A destroyed
Node B destroyed
GC collected 2 objects
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
gc.collect() 能检测并回收循环引用,即使其中对象有 __del__ 方法。
总结:
__new__决定对象创建,优先级高于__init__。__init__初始化对象属性,即使单例也会多次调用。__del__在对象销毁时调用,但调用时机不确定(取决于 GC)。gc.collect()可以演示循环引用下的销毁行为。
2340

被折叠的 条评论
为什么被折叠?



