weakref.ref 不能直接当函数用,因其返回 None 时再调用会报 TypeError;正确做法是每次使用前检查 ref() is not None。

weakref.ref 为什么不能直接当函数用
很多人写 weakref.ref(obj)() 想直接取值,结果遇到 TypeError: 'NoneType' object is not callable。这是因为 weakref.ref 返回的是一个可调用对象,但它的返回值是被引用对象(如果还活着),不是它自己;而一旦原对象被回收,ref() 就返回 None,再对 None 调用就会报错。
正确做法是先判空再用:
import weakref
<p>obj = [1, 2, 3]
ref_obj = weakref.ref(obj)
if ref_obj() is not None:
print(ref_obj()) # [1, 2, 3]
else:
print("对象已被回收")</p>- 永远不要假设
ref()一定返回有效对象 - 在循环、回调、缓存等长期存活的代码路径里,必须每次使用前检查
ref() is not None -
weakref.ref本身不阻止 GC,但它也不参与引用计数——这点和__weakref__属性不同
weakref.WeakKeyDictionary 适合做“以对象为键”的缓存
当你想按某个实例(比如一个 Widget 类对象)缓存计算结果,又不想让它因为被字典持有而无法释放,WeakKeyDictionary 是标准解法。它内部用弱引用来持 key,key 被销毁后,对应项自动从字典中消失。
常见误用是把它当普通 dict 用,忘了 key 必须是可哈希且支持弱引用的类型(比如不能是 list 或 dict):
立即学习“Python免费学习笔记(深入)”;
import weakref <p>cache = weakref.WeakKeyDictionary() widget = object() cache[widget] = "render_result"</p><h1>widget 被 del 后,cache 中该项自动消失</h1><p>del widget print(len(cache)) # 0</p>
- key 必须是自定义类实例或内置不可变类型(如
str,int),不能是list、dict、set等不支持弱引用的类型 - 如果你需要以不可哈希对象为 key,得改用
WeakValueDictionary+ 自定义 ID 映射,而不是硬塞 - 它不支持
.keys()遍历时的强一致性:遍历中 key 可能被回收,导致RuntimeError: dictionary changed size during iteration
weakref.finalize 怎么安全注册对象销毁回调
weakref.finalize 是比 __del__ 更可靠的对象清理机制,尤其适合资源释放(如关闭文件描述符、断开 socket)。它不依赖对象是否定义了 __del__,也不受 GC 循环引用影响。
但要注意:finalize 回调只保证“最多执行一次”,且执行时机不确定(通常在对象内存被回收时),所以不能在里面做耗时或阻塞操作:
import weakref import tempfile <p>f = tempfile.NamedTemporaryFile(delete=False) finalizer = weakref.finalize(f, lambda f: f.close())</p><h1>f.close() 会被调用,即使没显式 close</h1><p>del f</p>
- 回调函数不能捕获对外部变量的强引用,否则可能延迟对象回收(比如在回调里引用了
self或全局 dict) - finalize 对象本身也要注意生命周期:如果 finalize 被 gc 掉,回调就永远不会触发;建议把它绑定到长生命周期对象上(如类属性、模块变量)
- 不要在 finalize 里 raise 异常,异常会被静默丢弃,且不会中断 GC 流程
循环引用场景下 weakref 如何破局
Python 的引用计数无法处理 A→B 且 B→A 的循环引用,GC 虽能清理,但时机不可控。典型例子是观察者模式里,被观察对象持有了观察者列表,观察者又反向引用被观察者。
这时把反向引用改成弱引用,就能让 GC 正常工作:
import weakref
<p>class Observer:
def <strong>init</strong>(self, subject):
self.subject_ref = weakref.ref(subject) # 不增加 subject 引用计数</p><p>class Subject:
def <strong>init</strong>(self):
self._observers = []</p><pre class='brush:python;toolbar:false;'>def attach(self, observer):
self._observers.append(observer)subject = Subject() observer = Observer(subject) subject.attach(observer)
删除 subject 后,observer.subject_ref() 返回 None,不会阻止 subject 被回收
del subject
- 不是所有循环都该拆——只有涉及长生命周期对象、或明确观测到内存不释放时才值得动
- 用
weakref.proxy替代ref可以省去每次调用(),但 proxy 访问不存在属性会直接抛ReferenceError,不如ref容错高 - PyPy 和某些 C 扩展对象(如 NumPy 数组)可能不完全支持弱引用,测试时要验证
ref(obj)() is not None是否稳定
真正难的不是加 weakref,而是判断哪里该加——往往要结合 gc.get_referrers() 和内存快照工具(如 tracemalloc)定位“本该释放却没释放”的对象链。


















