必须使用weakref的场景是观察者回调、树节点双向引用、缓存键值互持等对象之间存在强引用且无外引用维持生存的场景;使用时,必须判断弱引用,避免闭包捕获,正确使用weakeydictionary。
Python 的 weakref 它可以帮助你绕过循环引用造成的垃圾回收失败,但可以直接应用 weakref.ref 或 weakref.WeakKeyDictionary 很容易踩到坑上——例如,在回调中访问已销毁的物体 ReferenceError,或误用弱引用代替生命周期管理。
只有当两个(或多个)对象相互持有强引用,并且没有外部引用来维持它们的生存时,它们才真正构成 GC 无法处理的循环。典型场景为:观察者模式中的回调绑定实例法、树节点父子双向引用、缓存中键值互持。
- 绑定类别方法后,将隐式持有
self如果观察者将回调存储在被观察者中,则形成一个循环 -
__del__不可靠,不能依靠它来清理资源;weakref.finalize这是一个更可控的替代品 - 单纯用
del或设为None间接引用可能会漏掉,weakref 是声明式解耦
weakref.ref 包裹回调,但必须检查是否还活着
直接调用弱引用对象是不安全的,可能已经回收利用。在执行逻辑之前,必须先调用返回的引用对象,判空。
import weakref
<p>class Observer:
def <strong>init</strong>(self, subject):</p><h1>引用弱,避免 subject 持有 observer 强引用</h1><pre class='brush:python;toolbar:false;'> self.callback = weakref.ref(self.on_update)
subject.add_observer(self.callback)
def on_update(self):
print("updated")在 subject 内部触发:
def notify_observers(self): for ref in self.observers: cb = ref() # 调用 ref() 得到原函数,可能是 None if cb is not None: cb()
- 不要写
ref()直接调用后,必须判空-否则抛ReferenceError - 绑定实例法时,
weakref.ref(obj.method)实际弱引用的是绑定对象,而不是绑定对象obj本身;如果需要保存obj,应弱引用obj并在回调中手动获取方法 -
lambda或者闭包会捕获变量,可能会意外延长对象的生命周期,谨慎使用
weakref.WeakKeyDictionary 反向映射的管理,但不适合普通 dict 用
它只对 key 做弱引用,value 仍然是强引用。适用于“以对象为键,存元数据”的场景,如给每个例子添加调试信息。
Python 3.14.2
Python 3.14.2是Python编程语言于2025年12月5日发布的稳定版本,属于3.14系列的第二次维护更新。该版本包含18个修复项目,重点解决多过程、数据和正则表达模块的回归问题,修复CVE-2025-12084等安全漏洞。这个版本标志着Python发展的一个重要里程碑,即自由线程模式(删除GIL)正式得到官方支持。
下载立即学习“Python免费学习笔记(深入);
import weakref <h1>正确:key 是实例,value 是轻量数据</h1><p>_debug_info = weakref.WeakKeyDictionary()</p><p>def attach_debug(obj, info): _debug_info[obj] = info # obj 销毁后,项目自动消失</p><h1>错误:value 它会导致另一个长生命周期的对象 obj 无法回收</h1><h1>_debug_info[obj] = big_cache_object # big_cache_object 持有 obj 强引用?</h1>
- value 不能反过来引用 key,否则,弱引用失效——WeakKeyDictionary 只管 key 的生命周期
- 遍历
dict.keys()时,某些 key 它可能在迭代的中间被回收,导致RuntimeError;应先转成 list:list(d.keys()) - 不支持
dict.setdefault()这种原子操作需要手动检查+赋值
最常被忽视的一点是:weakref 解决了引用计数归零前的循环堵塞,但不会改变对象本身的销毁时机。如果对象意外持有,如全局变量、封闭包装、局部线程存储等,weakref 也救不了-必须先确认泄漏源真的在那里 weakref 覆盖范围内。