weakvaluedictionary和weakkeydictionary不防止内存泄漏,仅避免缓存阻碍gc回收;值/键须支持弱引用,初始化需逐个赋值,遍历与多线程操作均有严格限制。

WeakValueDictionary 和 WeakKeyDictionary 本身不“防止”内存泄漏,它们只是让缓存不成为对象存活的障碍——只要值(或键)不再被其他地方强引用,GC 就能回收它,字典条目随之自动消失。
WeakValueDictionary 初始化必须逐个赋值,不能传字典
构造器不支持批量注册弱引用,因为弱引用必须在设值时动态建立。传入字典会直接报错,哪怕值本身合法。
-
weakref.WeakValueDictionary({"k": obj})→TypeError: cannot create weak reference to 'int' object(即使obj是类实例,也照样失败) - 正确写法:
cache = weakref.WeakValueDictionary(),然后cache["k"] = obj - 值对象必须支持弱引用:类实例、
list、dict、函数等有__weakref__属性的类型;int、str、tuple等不可变内置类型一存就崩
遍历时 cache[k] 可能触发 KeyError
WeakValueDictionary 不保证遍历一致性。某个值可能在 for k in cache: 过程中被 GC 回收,导致下一次 cache[k] 报 KeyError。
- 危险写法:
for k in cache: print(cache[k])→ 中途可能崩 - 安全写法:
v = cache.get(k); if v is not None: use(v) -
cache.values()返回的是weakref.ref对象列表,不是原始值;必须调用v()并判空,不能直接当值用 - 遍历中禁止增删键值对,否则抛
RuntimeError: dictionary changed size during iteration
WeakKeyDictionary 的 key 必须可哈希且支持弱引用
它适合“以对象为键”的缓存场景,比如按 GUI widget 实例缓存渲染结果。但 key 类型限制比普通 dict 严格得多。
- 支持的 key:自定义类实例、
str、int等可哈希且支持弱引用的类型 - 不支持的 key:
list、dict、set—— 它们默认不支持弱引用,强行塞进去会报TypeError - 若需用不可哈希对象作 key,得自己造 ID(如
id(obj)或hash((type(obj), id(obj)))),再配合WeakValueDictionary - 遍历时同样不保证一致性:
for k in cache.keys():中k可能在下一轮迭代前被回收,引发RuntimeError
多线程写入 WeakValueDictionary 必须加锁
它不是线程安全的。多个线程同时 cache[k] = obj 或 del cache[k],可能破坏内部状态或漏删条目。
- 最简方案:用
threading.RLock()包裹所有写操作 - 读操作(
cache.get(k))虽不修改结构,但若和写并发,仍可能读到中间态(如刚注册 ref 但回调未触发) - 如果读远多于写,可考虑读写锁(
threading.ReadWriteLock,需第三方库或手写)
真正容易被忽略的是:弱字典只解决“缓存不阻碍回收”这一个问题。如果你的值对象还被其他地方(比如日志模块、全局监听器、未清理的回调)强引用着,那它照样不会被回收——WeakValueDictionary 不会帮你扫清所有强引用路径。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











