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

Python 由于循环引用,未调用弱引用回调,python,lambda,weak-references,Python,Lambda,Weak References,我正在尝试为具有循环引用的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 = we

我正在尝试为具有循环引用的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
A created
An A deleted:a1
B created
删除循环引用会使lambda回调工作('打印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
B created
A created
An A deleted:a1
B created

知道为什么lambda回调不能处理循环引用吗?

循环引用会自动清除。也有一些例外,例如定义
\uu del\uu
方法的类

通常,使用时不需要定义
\uuu del\uuu
方法

 self._wr = weakref.ref(self, lambda wr, n = self.name: del_A(n))  
self._wr = weakref.ref(self, del_A(self.name))
只有当
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, lambda wr, n = self.name: del_A(n))  
self._wr = weakref.ref(self, del_A(self.name))

然后您的回调为
None
del_A(self.name)
不是对函数的引用,而是函数调用本身。因此
delu A(self.name)
立即打印
A deleted:a1
(在
a1
真正完成之前),并返回值
None
,这成为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()是必需的,否则将在调用回调之前删除终结器。显然,必须正确管理终结器,以避免生成大量弱引用,但这是另一个问题。

谢谢您的回答。我应该澄清我问题的措辞。我知道a.other=b不切换a,但我希望在代码块末尾,a和b超出范围,因此被垃圾收集/最终确定。为什么不调用lambda回调?当我删除循环引用时,会调用lambda回调,这让我感到困惑。但是您对self.\u wr=weakref.ref(self,del_A(self.name))不起作用的解释是正确的。del_A()是函数调用,而不是引用,因此它不是传递回调的正确方式。谢谢。谢谢你的回复,但我相信这并不能回答我的问题。我使用弱引用来避免del方法。