You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优雅结合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:47:09