weakref.ref适用于子持父且子不应延长父生命周期的场景,如gui widget与处理器、观察者模式;父存子用强引用,子存父用weakref.ref并判空调用,避免用于内置类型或不可弱引用对象。

weakref 能破循环引用,但必须用对地方——不是所有循环都靠它解决,也不是加了 weakref.ref 就万事大吉。
什么时候该用 weakref.ref 而不是直接存对象?
典型场景是「父持有子,子又反向持有父」且子生命周期不应延长父的生命周期。比如 GUI 中的 widget 和它的事件处理器、观察者模式里的 observer 和 subject、缓存中 key 持有 value 的反向引用。
直接存强引用会让双方互相拖住,GC 无法回收;而用 weakref.ref 存反向那一端(通常是子持父),就能让父对象在无其他强引用时被及时释放。
- 父类里存子对象:用普通引用(强引用)——子本就该随父存在
- 子类里需要访问父:用
weakref.ref(parent),调用前先检查parent_ref() is not None - 别对内置类型(如
int、str)或不可弱引用对象(如某些 C 扩展类)调用weakref.ref,会抛TypeError
weakref.WeakKeyDictionary 和 weakref.WeakValueDictionary 怎么选?
两者都自动清理失效键/值,但触发时机和适用逻辑完全不同:
-
WeakKeyDictionary:key 是弱引用,value 是强引用。适合「以某个对象为索引存数据,但不希望因这个索引存在而阻止对象被回收」——比如给每个 socket 对象缓存配置,socket 关闭后缓存自动消失 -
WeakValueDictionary:value 是弱引用,key 是强引用。适合「多个地方需要共享同一个对象,但不想让它长期驻留内存」——比如按 ID 缓存模型实例,实例被显式 del 或超出作用域后,字典里对应项自动移除 - 二者都不支持不可哈希类型作 key(如
list、dict),也不支持不可弱引用对象作 weak key/value
为什么用了 weakref 还有内存泄漏?常见掉坑点
weakref 本身不泄漏,但误用会让它形同虚设:
- 忘了调用弱引用对象:写成
self.parent_ref = weakref.ref(parent),却在后续代码里直接写self.parent_ref.xxx()(报TypeError: 'callable' object is not subscriptable);正确写法是parent = self.parent_ref(),再判空使用 - 把弱引用存进了全局容器(如模块级 dict 或 class 变量),导致 key/value 永远不会被清掉——
WeakKeyDictionary必须是实例属性,不能是类属性 - 在
__del__方法里试图访问弱引用指向的对象:此时对象可能已被析构,self.parent_ref()返回None,再调用其方法会出错;更安全的做法是在业务逻辑中主动清理,而非依赖__del__
一个最小可验证的循环引用破除示例
下面这段代码模拟了典型的父子循环引用,对比加与不加 weakref 的行为差异:
import weakref import gc <p>class Parent: def <strong>init</strong>(self): self.child = Child(self)</p><p>class Child: def <strong>init</strong>(self, parent): self.parent_ref = weakref.ref(parent) # 关键:不用强引用</p><p>p = Parent() print(len(gc.get_objects())) # 数量较大 del p gc.collect() print(len(gc.get_objects())) # 明显减少 —— 父对象被回收了</p><h1>如果 Child 改成 self.parent = parent(强引用),则 del p 后父对象仍残留</h1>
注意:实际项目中别依赖 gc.collect() 强制回收,重点是确保没有强引用链闭合。真正难缠的是多层嵌套+闭包+ lambda 组合出来的隐式引用,那种得用 objgraph 或 gc.get_referrers() 追踪。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











