我正在尝试为具有循环引用的Python类编写终结器。我发现弱引用回调是way to go。不幸的是,似乎我用作回调的lambda从未被调用过。例如,运行此代码:
def del_A(name):
print('An A deleted:' + name)
class A(object):
def __init__(self, name):
print('A created')
self.name = name
self._wr = weakref.ref(self, lambda wr, n = self.name: del_A(n))
class B(object):
def __init__(self):
print('B created')
if __name__ == '__main__':
a = A('a1')
b = B()
a.other = b
b.other = a
返回:
A created
B created
删除循环引用使lambda回调起作用('A A deleted:a1'被打印)。通过简单的函数调用替换lambda也可以,但是在初始化弱引用时修复参数值,而不是在调用回调时:
self._wr = weakref.ref(self, del_A(self.name))
...
a = A('a1')
a.name = 'a2'
b = B()
a.other = b
b.other = a
返回:
A created
An A deleted:a1
B created
知道为什么lambda回调不能用于循环引用吗?
答案 0 :(得分:2)
使用时
self._wr = weakref.ref(self, lambda wr, n = self.name: del_A(n))
只有在self
即将完成时才会调用回调。
没有调用回调的原因是因为
a = A('a1')
b = B()
a.other = b # This gives a another attribute; it does not switch `a` away from the original `a`
b.other = a
不会导致a
最终确定。原始a
仍然存在。
如果您将代码更改为
,则会调用回调a = A('a1')
b = B()
a = b
b = a
使用时
self._wr = weakref.ref(self, del_A(self.name))
然后你的回调是None
。 del_A(self.name)
不是对函数的引用,它是函数调用本身。因此del_A(self.name)
会立即打印An A deleted:a1
(在a1
真正定稿之前),并返回值None
,这将成为弱参数的默认回调。
答案 1 :(得分:2)
我想我终于找到了在弱引用存在的情况下不调用回调的原因:
如果"weakref object dies before the object it references"
,则不会调用弱引用回调当删除循环引用时,似乎在回调有机会被调用之前删除了类A的弱引用属性。一种解决方案是将终结器(即弱引用及其回调)附加到终结器列表。例如:
def del_A(name):
print('An A deleted:' + name)
class A(object):
def __init__(self, name, finalizers):
print('A created')
self.name = name
finalizers.append(weakref.ref(self, lambda wr, n = self.name: del_A(n)))
class B(object):
def __init__(self):
print('B created')
def do_work(finalizers):
a = A('a1', finalizers)
b = B()
a.other = b
b.other = a
if __name__ == '__main__':
finalizers = []
do_work(finalizers)
将打印:
A created
B created
An A deleted:a1
请注意,do_work()是必需的,否则在回调有机会被调用之前,终结器会被删除。显然,必须妥善管理终结器以避免构建大量弱引用,但这是另一个问题。
答案 2 :(得分:0)
自动清理循环引用。有一些例外,例如定义__del__
方法的类。
通常您不需要定义__del__
方法