由于循环引用,未调用弱引用回调

时间:2010-02-19 11:47:57

标签: python lambda weak-references

我正在尝试为具有循环引用的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回调不能用于循环引用吗?

3 个答案:

答案 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))

然后你的回调是Nonedel_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__方法