当前位置: 首页 > 图灵资讯 > 行业资讯> 如何在Python中利用weakref弱引用防止循环引用导致的内存泄漏?

如何在Python中利用weakref弱引用防止循环引用导致的内存泄漏?

来源:图灵python
时间: 2026-07-22 17:05:51
必须使用weakref的场景是观察者回调、树节点双向引用、缓存键值互持等对象之间存在强引用且无外引用维持生存的场景;使用时,必须判断弱引用,避免闭包捕获,正确使用weakeydictionary。

Python 的 weakref 它可以帮助你绕过循环引用造成的垃圾回收失败,但可以直接应用 weakref.refweakref.WeakKeyDictionary 很容易踩到坑上——例如,在回调中访问已销毁的物体 ReferenceError,或误用弱引用代替生命周期管理。

必须何时使用 weakref?并非所有的循环引用都需要它

只有当两个(或多个)对象相互持有强引用,并且没有外部引用来维持它们的生存时,它们才真正构成 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 覆盖范围内。