bigywater.tech
Python · 30

weakref:弱引用为什么不会阻止对象被回收

验证weakref.ref创建的弱引用不会增加对象引用计数,del原对象后引用立刻失效、绑定的回调自动触发,以及WeakValueDictionary怎么自动清理失效的key。

None
原对象del后弱引用立刻变成的值
自动触发
绑定的回调在对象销毁那一刻执行
自动清理
WeakValueDictionary不需要手动清理失效key

做的事:给一个对象建一个不增加引用计数的引用

普通的变量赋值都会增加引用计数,weakref.ref不会。

代码import weakref class Widget: def __init__(self, name): self.name = name w = Widget("battery") r = weakref.ref(w) print(r()) # 弱引用对象本身 del w print(r()) # 原对象被删除后,弱引用还能用吗

验证:del之后弱引用立刻变成了None

这跟普通变量引用的行为完全不同。

结果before del, r(): <__main__.Widget object at 0x106229400> after del+gc, r(): None

验证:weakref.ref(w) 创建的r不会让Widget对象的引用计数+1——如果它会,那么del w之后对象的引用计数依然是1(还被r引用着),对象就不会被销毁,r()也就还能拿到原对象。但结果是del之后对象立刻被销毁,r()变成None——这正是弱引用的定义:它能"看到"对象,但不算在决定对象生死的引用计数里。

验证:绑定的回调函数在对象被销毁那一刻被调用

weakref.ref可以带一个回调参数。

代码与结果collected = [] def on_collect(ref): collected.append(True) r2 = weakref.ref(target, on_collect) del target gc.collect() print(collected) # 结果: [True]

回调on_collect在target对象被销毁的那一刻自动触发,收到的参数是弱引用对象本身(不是原对象,这时候原对象已经不存在了)——这正是很多缓存、资源池、监听器注销机制依赖的钩子:不需要在业务代码里显式调用"注销",对象真的没人再用了的那一刻,注册在弱引用上的清理逻辑会自动跑。

验证:WeakValueDictionary自动清理失效的key

最常见的弱引用实战场景是给对象做缓存字典,又不想缓存本身导致对象永远不被回收。

结果d = weakref.WeakValueDictionary() d["k"] = obj before del: "k" in d = True, d.get("k") = <__main__.Big object> del obj gc.collect() after del: "k" in d = False
用普通dict做缓存有一个经典陷阱:只要对象的引用还挂在dict的value位置上,这个对象就永远不会被垃圾回收,哪怕业务代码里所有其它地方都已经不再需要它——这是一种容易被忽略的内存泄漏。WeakValueDictionary的做法是只对value持有弱引用,一旦某个value对象在别处没有任何强引用了,这个键值对会自动从字典里消失,"k" in d直接变成False,不需要手动写清理逻辑去扫描哪些value已经失效——这正是本系列前面讲对象生命周期、GIL、引用计数这些话题时反复出现的同一个底层机制:Python对象的生死完全由引用计数(加上gc处理循环引用)决定,弱引用是唯一一种"能访问对象、但不介入这场生死判定"的引用方式。

分步演示:弱引用怎么跟着对象销毁

每一步都是本文上面用weakref模块跑出来的原始结果。

弱引用不参与对象生死判定,但能感知生死
这一步新验证的场景

    本文用到的工具

    • 本机 Python 3.14.7
    • weakref.ref / weakref.WeakValueDictionary:验证弱引用不增加引用计数、回调触发时机、自动清理失效key
    京ICP备2026067502号-1京公网安备11010502063487号