【问题标题】:Weak reference callback is not called because of circular references由于循环引用,不调用弱引用回调
【发布时间】:2010-02-19 11:47:57
【问题描述】:

我正在尝试为具有循环引用的 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 回调起作用(打印“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 回调不适用于循环引用吗?

【问题讨论】:

    标签: python lambda weak-references


    【解决方案1】:

    当你使用时

     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,这将成为弱引用的默认回调。

    【讨论】:

    • 感谢您的回答。我应该澄清我的问题的措辞。我知道 a.other = b 不会切换 a,但我希望在代码块的末尾,a 和 b 超出范围,因此被垃圾收集/完成。为什么不调用 lambda 回调呢?当我删除循环引用时,会调用 lambda 回调,这让我很困惑。
    • 但是您对 self._wr = weakref.ref(self, del_A(self.name)) 为什么不起作用的解释是正确的。 del_A() 是函数调用,而不是引用,因此它不是传递回调的正确方法。谢谢。
    【解决方案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() 是必需的,否则在回调有机会被调用之前终结器被删除。显然,终结器必须妥善管理以避免构建大量弱引用,但这是另一个问题。

    【讨论】:

      【解决方案3】:

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

      通常不需要定义__del__ 方法

      【讨论】:

      • 感谢您的回复,但我相信它并不能解决我的问题。我使用弱引用来避免 del 方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多