__del__ 不是可靠清理入口,绝大多数关键资源释放场景都不该用它;其调用时机不可控,不保证执行、顺序或时间点,易因循环引用、os._exit()、异常帧对象残留、模块卸载等失效,且GC顺序导致访问已回收对象时异常被静默忽略;应改用显式close()、with上下文管理器或weakref.finalize()。

__del__ 不是可靠清理入口,绝大多数关键资源释放场景都不该用它
__del__ 调用时机完全不可控
Python 不保证 __del__ 一定执行,也不保证执行顺序和时间点。常见失效场景包括:
- 程序调用
os._exit()时,解释器直接退出,所有__del__被跳过 - 对象被循环引用持有时,引用计数永不归零,
__del__永远不触发(哪怕gc.collect()成功回收) - 异常发生后帧对象(
<frame at>)长期持有对象引用,尤其在 mock 测试中高频出现 - 模块卸载阶段,类本身可能已被清空,
__del__中访问类变量(如NewClass.num_count)会抛AttributeError: 'NoneType' object has no attribute ...
__del__ 中访问其他对象极易出错
GC 回收顺序由内部算法决定,不按创建/销毁逻辑排列。这意味着:
- A 的
__del__可能先于 B 执行,但 A 中又尝试访问 B 的属性或方法 →AttributeError - 若 B 已被回收,A 的
__del__访问B.ref就会失败,且该异常会被静默忽略(只打印到sys.stderr) - 无法在
__del__中安全调用print()、logging.info()或任何可能触发新 GC 的操作
del obj 并不等于触发 __del__
del obj 只是删除名字绑定,把引用计数减 1;是否触发 __del__ 完全取决于该对象当前引用数是否真为 0:
立即学习“Python免费学习笔记(深入)”;
- 用
gc.get_referrers(obj)查谁还在引用它,常看到<frame>、mock.Mock、threading.local()实例 -
sys.getrefcount(obj)返回值要减 1(因传参本身增加一次引用),才反映真实计数 - 即使你
del a和del b,若 a 和 b 彼此强引用,两者引用数都还是 1 →__del__都不会运行
替代方案比 __del__ 更可靠
真正需要资源清理时,应绕过 __del__,改用确定性机制:
- 显式调用
close()、shutdown()等方法,并在文档里明确要求使用者调用 - 用上下文管理器(
with语句),确保退出时必然执行__exit__ - 对文件、socket、数据库连接等,优先使用标准库自带的上下文支持(如
open()、sqlite3.connect()) - 若必须自动清理,可结合
weakref.finalize()—— 它不依赖引用计数归零,且可注册回调而不受 GC 顺序干扰
最危险的不是写错 __del__,而是以为它“差不多能用”。一旦涉及文件关闭、连接释放、锁释放等操作,漏掉一次就可能造成泄漏或死锁。


















