Python中ref与WeakMethod比较结果异常问题求助
问题:WeakMethod与ref实例在集合中被判定为相等的原因?
我使用set存储可调用对象的弱引用,这些对象包括函数、实现__call__方法的可调用实例以及绑定方法。根据Python官方文档,我对绑定方法使用weakref.WeakMethod,对其他可调用对象使用weakref.ref,但遇到了如下问题:
示例代码:
from weakref import ref, WeakMethod class Callbacks: def method(self, *args, **kwargs): print('method()') def __call__(self, *args, **kwargs): print('__call__()') cb = Callbacks() listeners = set() listeners.add(ref(cb)) print(f'#listeners: expected = 1, actual = {len(listeners)}') listeners.add(WeakMethod(cb.method)) print(f'#listeners: expected = 2, actual = {len(listeners)}')
运行结果:
#listeners: expected = 1, actual = 1 #listeners: expected = 2, actual = 1
经排查发现,WeakMethod(cb.method)与ref(cb)相等,尽管cb.method != cb,请问这是什么原因?
原因分析
这是因为WeakMethod的相等性判断逻辑是基于它引用的绑定方法所属的实例对象,而非绑定方法本身。
具体细节:
- 创建
WeakMethod(cb.method)时,它内部实际维护了对cb实例和method函数的引用,但在做相等性比较时,会优先对比引用的实例对象。 ref(cb)直接引用的就是cb实例。- WeakMethod的
__eq__方法实现中,会和其他weakref类型(比如ref实例)比较最终指向的对象是否一致。只要两个弱引用的目标是同一个实例,就会被判定为相等。
你可以通过下面的代码验证这个结论:
r = ref(cb) wm = WeakMethod(cb.method) print(r() is wm()) # 输出True,因为wm()返回的是cb实例本身
因此当你把这两个对象加入set时,set会认为它们是同一个元素,第二个add操作不会增加集合的长度。
如果需要让它们在set中被视为不同元素,你可以自定义一个包装类,重新实现__eq__和__hash__方法,区分开是实例本身的弱引用还是绑定方法的弱引用。
内容的提问来源于stack exchange,提问作者bavaza
相关产品推荐
相关产品推荐

