weakref能解决父-子对象互相强引用导致的内存泄漏问题。通过weakref.ref将反向引用改为弱引用,或用WeakKeyDictionary缓存对象映射,可使对象在无其他强引用时被及时回收;还需警惕lambda闭包等隐式强引用。

weakref能解决什么循环引用问题
当两个对象互相持有对方的强引用时,Python的垃圾回收器(GC)无法自动释放它们,即使外部已无其他引用指向它们。典型场景是父对象持有一个子对象列表,而子对象又通过parent属性反向引用父对象——这种结构在树形结构、观察者模式、缓存容器中很常见。
使用weakref.ref或weakref.WeakKeyDictionary等可以打破强引用链,让对象在无其他强引用时被及时回收。
用weakref.ref包装反向引用
这是最直接的方式:把子对象里的parent字段从强引用改为弱引用回调。
- 定义子类时,用
weakref.ref初始化self._parent_ref,而不是直接赋值self.parent = parent - 访问时需调用
self._parent_ref()(注意括号),返回parent对象或None(若已被回收) - 不要对
weakref.ref对象做==或is比较,它本身不是原对象 - 如果需要频繁访问且不想每次调用
(),可加一个property:@property def parent(self): return self._parent_ref()但要注意:这个property返回的是临时强引用,不影响生命周期;真正决定是否存活的是
_parent_ref本身。立即学习“Python免费学习笔记(深入)”;
用
weakref.WeakKeyDictionary替代普通字典缓存当你用字典缓存“对象 → 其他数据”映射(比如为每个
Widget实例缓存渲染状态),而字典作为全局变量长期存在,就会阻止Widget被回收。- 把
dict换成weakref.WeakKeyDictionary,键必须是可哈希对象(如自定义类实例),值可以是任意类型 - 键对象一旦被回收,对应条目自动从字典中消失,无需手动清理
- 不能用不可哈希类型(如
list、dict)作键,否则抛出TypeError: unhashable type - 遍历时要意识到:迭代过程中可能有键被回收,导致
RuntimeError: dictionary changed size during iteration,应先转成list(dict.items())再遍历
容易忽略的坑:lambda和闭包中的隐式强引用
写回调函数时,很容易无意中引入强引用。例如:
# ❌ 错误:lambda捕获了self,形成强引用循环 self.timer = threading.Timer(1.0, lambda: self.on_timeout()) <h1>✅ 正确:用weakref断开</h1><p>self._weak_self = weakref.ref(self) self.timer = threading.Timer(1.0, lambda: self._weak_self() and self._weak_self().on_timeout())
类似情况也出现在
functools.partial、信号槽连接(如PyQt的connect)、装饰器缓存中。只要闭包里引用了可能构成循环的对象,就得检查是否需要弱化。另外,
weakref.finalize适合做资源清理,但它不保证一定执行(比如解释器退出时可能跳过),所以不能替代__del__做关键释放,只适合日志、统计等非关键操作。 - 把


















