为什么我的弱引用指向方法时无效?

2 投票
2 回答
1355 浏览
提问于 2025-04-16 14:12

可能重复的问题:
为什么这个绑定方法的弱引用不起作用?

我在使用弱引用(weakrefs)来实现观察者模式时,发现了一个有趣的现象。如果我创建一个对象,并把它的一个方法作为观察者添加到一个可观察对象(Observable)中,这个引用几乎立刻就失效了。有没有人能解释一下这是怎么回事?

我也想听听大家对这可能是个坏主意的看法。我决定不使用弱引用,而是确保在使用完后正确地调用 Observable.removeobserver 来清理,但我对此非常好奇。

这是代码:

from weakref import ref
class Observable:
    __observers = None
    def addobserver(self, observer):
        if not self.__observers:
            self.__observers = []
        self.__observers.append(ref(observer))
        print 'ADDING observer', ref(observer)

    def removeobserver(self, observer):
        self.__observers.remove(ref(observer))

    def notify(self, event):
        for o in self.__observers:
            if o() is None:
                print 'observer was deleted (removing)', o
                self.__observers.remove(o)
            else:
                o()(event)

class C(Observable):
    def set(self, val):
        self.notify(val)

class bar(object):
    def __init__(self):
        self.c = C()
        self.c.addobserver(self.foo)
        print self.c._Observable__observers

    def foo(self, x):
        print 'callback', x  #never reached

b = bar()
b.c.set(3)

这是输出结果:

ADDING observer <weakref at 0xaf1570; to 'instancemethod' at 0xa106c0 (foo)>
[<weakref at 0xaf1570; dead>]
observer was deleted (removing) <weakref at 0xaf1570; dead>

主要需要注意的是,在调用 addobserver 之后的打印语句显示,弱引用已经失效了。

2 个回答

3

每次你访问一个实例的方法,比如说 obj.m,系统会生成一个叫做“绑定方法”的包装器。这个包装器可以被调用,并且在调用时会把 self(也就是 obj)作为第一个参数传进去。这种方式很巧妙,因为它让你可以“隐式”地传递 self,而且让实例方法能够被使用。但这也意味着,每次你输入 obj.m 时,都会创建一个新的(非常轻量级的)对象。如果你没有保留一个(非弱引用的)指针指向它,它就会被垃圾回收掉,因为没有人会帮你保留它。

4

每当你引用一个对象的方法时,会发生一些神奇的事情,而正是这种魔法让你遇到了问题。

具体来说,Python会在对象的类中查找这个方法,然后把它和对象本身结合起来,形成一种叫做“绑定方法”的可调用对象。每次你执行例如self.foo这样的表达式时,都会创建一个新的绑定方法实例。如果你立刻对这个绑定方法使用弱引用(weakref),那么就没有其他地方再引用这个绑定方法了(尽管对象和类的方法仍然有活跃的引用),这样弱引用就会失效。

想了解解决办法,可以查看这个ActiveState的代码片段

撰写回答