3

我正在尝试为具有循环引用的 Python 类编写终结器。我发现弱引用回调是要走的路。不幸的是,我用作回调的 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 回调起作用(打印“An 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 回调不适用于循环引用吗?

4

3 回答 3

3

当你使用

 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真正完成之前),并返回 value None,这将成为 weakref 的默认回调。

于 2010-02-19T12:40:29.347 回答
3

我想我终于找到了在存在弱引用的情况下不调用回调的原因:

如果“weakref 对象在它引用的对象之前死亡”,则不会调用弱引用回调

似乎在删除循环引用时,在回调有机会被调用之前就删除了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() 是必需的,否则在回调有机会被调用之前,终结器会被删除。显然,终结器必须妥善管理以避免构建大量弱引用,但这是另一个问题。

于 2010-03-07T16:48:30.540 回答
0

循环引用会自动清理。有一些例外,例如定义__del__方法的类。

通常你不需要定义一个__del__方法

于 2010-02-19T11:58:46.107 回答