如何优雅结合WeakSet与WeakMethod存储Python绑定方法?
如何优雅结合WeakSet与WeakMethod存储绑定方法的弱引用
我需要利用weakref.WeakSet的自动清理功能,但要存储绑定方法,因此得借助weakref.WeakMethod。但直接把WeakMethod实例存入WeakSet会得到空集合,示例代码及运行结果如下:
import weakref class C: def method(self): pass ci = C() print("weakMethod:", weakref.WeakMethod(ci.method)) print("s1:", weakref.WeakSet([C.method])) print("s2:", weakref.WeakSet([ci.method])) print("s3:", weakref.WeakSet([weakref.WeakMethod(ci.method)]))
Python 3.12.2运行输出:
weakMethod: <weakref at 0x7f569e9308d0; to 'C' at 0x7f569e96dca0> s1: {<weakref at 0x7f56ac12a520; to 'function' at 0x7f569e98ade0 (method)>} s2: set() s3: set()
其中s2为空符合预期(绑定方法是临时对象,无法被直接弱引用),s1能正常存储未绑定方法的弱引用,但s3存入WeakMethod后为空——原因是WeakSet会对元素本身创建弱引用,而传入的WeakMethod实例没有其他强引用支撑,被GC立即回收了。
优雅解决方案:自定义WeakMethodSet类
我们可以实现一个专门适配WeakMethod的集合类,模仿WeakSet的自动清理逻辑,当绑定方法所属的实例被回收时,自动移除对应条目:
import weakref class WeakMethodSet: def __init__(self, methods=None): self._refs = set() self._finalizers = {} if methods: for meth in methods: self.add(meth) def add(self, method): # 创建绑定方法的WeakMethod引用 wr = weakref.WeakMethod(method) # 获取绑定的实例对象 obj = method.__self__ # 定义实例回收时的清理逻辑 def cleanup(ref): self._refs.discard(ref) del self._finalizers[id(ref)] # 给实例添加终结器,确保实例销毁时触发清理 finalizer = weakref.finalize(obj, cleanup, wr) self._refs.add(wr) self._finalizers[id(wr)] = finalizer def discard(self, method): wr = weakref.WeakMethod(method) if wr in self._refs: self._refs.discard(wr) self._finalizers[id(wr)].detach() del self._finalizers[id(wr)] def __contains__(self, method): wr = weakref.WeakMethod(method) return wr in self._refs def __iter__(self): # 迭代时过滤已失效的引用 for wr in self._refs.copy(): meth = wr() if meth is not None: yield meth def __len__(self): # 仅统计有效的绑定方法引用 return sum(1 for wr in self._refs if wr() is not None) def __repr__(self): return f"WeakMethodSet({list(self)})"
使用示例
class C: def method(self): print("method called") ci = C() ci2 = C() wm_set = WeakMethodSet([ci.method, ci2.method]) print(wm_set) # 输出: WeakMethodSet([<bound method C.method of <__main__.C object at 0x...>>, <bound method C.method of <__main__.C object at 0x...>>]) # 销毁其中一个实例 del ci import gc gc.collect() print(wm_set) # 输出: WeakMethodSet([<bound method C.method of <__main__.C object at 0x...>>]) print(len(wm_set)) # 输出: 1
方案优势
- 完全复刻WeakSet的自动清理特性,无需手动管理引用生命周期
- 直接操作绑定方法,无需手动创建WeakMethod实例
- 提供标准集合的核心方法(
add/discard/__contains__等),使用体验与原生集合一致 - 自动过滤失效引用,迭代时仅返回有效的绑定方法
内容的提问来源于stack exchange,提问作者Tom Pohl
相关产品推荐
相关产品推荐

