为什么我的弱引用指向方法时无效?
可能重复的问题:
为什么这个绑定方法的弱引用不起作用?
我在使用弱引用(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的代码片段。