WeakSet中的WeakMethod

2 投票
1 回答
43 浏览
提问于 2025-04-14 18:29

我想使用 weakref.WeakSet 的功能,但我想在这个集合里存储绑定的方法,所以我必须使用 weakref.WeakMethod

下面是一个简化的例子:

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()

从第一行可以看到,WeakMethod 按照预期工作,但把它存储在 WeakSet 中却得到了一个空的 s3

顺便提一下:s2 是空的,这很正常,但像 s1 这样存储一个未绑定方法的弱引用是可以的。

显而易见的解决办法是:用一个普通的 set 来代替 WeakSet,然后自己实现它的功能。

问题:有没有更优雅的方法来结合 WeakSetWeakMethod 的功能?

1 个回答

0

这是我在使用观察者模式时目前的“明显”解决办法:

class Observable:
    def __init__(self):
        self.observers = set()

    def observe(self, method):
        def remote_observer(observer):
            print(f"removed {observer} from observers")
            self.observers.remove(observer)

        self.observers.add(weakref.WeakMethod(method, remote_observer))

    def unobserve(self, func):
        self.observers.discard(func)

    def call_observers(self):
        for observer in self.observers:
            observer()()


observable = Observable()


class ACME:
    def __init__(self):
        for _ in range(5):
            observable.observe(self.observer)

    def observer(self):
        print("ACME observer called")

acme = ACME()
print("### expecting one observer to be called ###")
observable.call_observers()
del acme
print("### expecting no observer to be called ###")
observable.call_observers()

这个办法的输出是:

### expecting one observer to be called ###
ACME observer called
removed <weakref at 0x7f1240feee50; dead> from observers
### expecting no observer to be called ###

注意到这里的 set 很有效,它把五个相同的观察者合并成了一个观察者,而 WeakMethod 则在执行 del acme 后移除了观察者。如果你把 WeakMethod 换成直接引用,那么即使执行了 del acme,它也会保留 acme 和它的观察者(这正是我想避免的情况)。

好吧,这个办法可以用,但我很好奇你是否有更优雅的解决方案,使用 WeakSet

撰写回答