如何在Python中实现支持弱引用/垃圾回收的对象驻留?
实现支持弱引用/垃圾回收的非字符串对象驻留
问题背景
需要为非字符串的不可变对象实现驻留(即相同内容的对象返回同一实例),替代仅支持字符串的sys.intern,但普通字典的强引用会导致内存无限增长;WeakSet虽支持自动回收,但查找效率极低;WeakKey/ValueDictionary也无法直接满足“弱引用+快速查找”的需求。
可行解决方案
方案1:哈希分组+WeakSet优化查找
核心思路是用哈希值对对象分组,将同一哈希的对象存入对应WeakSet,查找时仅遍历相同哈希的候选对象,大幅提升效率,同时WeakSet会自动回收无强引用的对象。
import weakref class WeakInterner: def __init__(self): # 键:对象哈希值,值:存储对应哈希对象的WeakSet self._hash_groups = {} def intern(self, obj): obj_hash = hash(obj) # 获取当前哈希对应的WeakSet,不存在则创建 group = self._hash_groups.setdefault(obj_hash, weakref.WeakSet()) # 在分组内查找相等的对象 for existing in group: if existing == obj: return existing # 无匹配对象,加入分组并返回原对象 group.add(obj) return obj
使用示例
# 定义一个自定义不可变类(需实现__eq__和__hash__) class ImmutableItem: def __init__(self, id, value): self._id = id self._value = value def __eq__(self, other): if not isinstance(other, ImmutableItem): return False return self._id == other._id and self._value == other._value def __hash__(self): return hash((self._id, self._value)) # 初始化驻留器 interner = WeakInterner() # 创建两个内容相同的对象 item1 = ImmutableItem(1, "test") item2 = ImmutableItem(1, "test") # 驻留后指向同一实例 interned1 = interner.intern(item1) interned2 = interner.intern(item2) print(interned1 is interned2) # 输出: True # 移除所有强引用,触发GC后对象会被回收 del item1, item2, interned1, interned2 import gc gc.collect() # 对应哈希分组已为空 print(len(interner._hash_groups[hash(ImmutableItem(1, "test"))])) # 输出: 0
关键注意事项
- 对象必须不可变:驻留对象若被修改,所有引用该实例的代码都会受到影响,这是驻留的固有风险,需自行把控。
- 实现正确的
__eq__和__hash__:自定义类必须手动实现这两个方法,确保哈希值与对象内容关联,且相等的对象哈希值一致。 - GC回收时机:WeakSet中的元素会在对象无强引用时自动移除,但GC不会立即触发,需等待自动GC或手动调用
gc.collect()。
内容的提问来源于stack exchange,提问作者Yakov Galka
相关产品推荐
相关产品推荐

