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

发布于 2024-08-22 08:05:48 字数 1030 浏览 6 评论 0原文

我正在尝试为具有循环引用的 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

returns:

A created
B created

删除循环引用会使 lambda 回调起作用(打印“An A returned: 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

returns:

A created
An A deleted:a1
B created

知道为什么 lambda 回调不适用于循环引用吗?

I'm trying to write a finalizer for Python classes that have circular references. I found out that weak reference callbacks are the way to go. Unfortunately, it seems the lambda I use as a callback is never called. For example, running this code:

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

returns:

A created
B created

Removing the circular reference makes the lambda callback works ('An A deleted: a1' is printed). Replacing the lambda by a simple function call works too, but the parameter value is fixed when initializing the weak reference, and not when calling the callback:

self._wr = weakref.ref(self, del_A(self.name))
...
a = A('a1')
a.name = 'a2'
b = B()
a.other = b
b.other = a

returns:

A created
An A deleted:a1
B created

Any idea why the lambda callback does not work with circular references?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一口甜 2024-08-29 08:05:48

当您使用

 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 returned:a1(在 a1 真正最终确定之前),并返回值 >None,成为弱引用的默认回调。

When you use

 self._wr = weakref.ref(self, lambda wr, n = self.name: del_A(n))  

the callback will only be called when self is about to be finalized.

The reason why the callback is not getting called is because

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

does not cause a to be finalized. The original a still exists.

The callback would be called if you changed the code to

a = A('a1')
b = B()
a = b
b = a

When you use

self._wr = weakref.ref(self, del_A(self.name))

then your callback is None. del_A(self.name) is not a reference to a function, it is a function call itself. So del_A(self.name) prints An A deleted:a1 immediately (before a1 is really finalized), and returns with the value None, which becomes the default callback for the weakref.

各自安好 2024-08-29 08:05:48

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

如果 "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() 是必要的,否则终结器会在回调有机会被调用之前被删除,显然,必须正确管理终结器以避免构建巨大的列表。弱引用,但这是另一个问题。

I think I finally found the reason why the callback is not called in the presence of a weak reference:

Weak reference callbacks are not called if the "weakref object dies before the object it
references"

It seems that when circular references are deleted, the weak reference attribute of class A is deleted before the callback has a chance to be called. One solution, is to append the finalizer (i.e., the weak reference and its callback) to a list of finalizers. For example:

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)

will print:

A created
B created
An A deleted:a1

Note that do_work() is necessary, otherwise finalizers gets deleted before the callbacks have a chance to be called. Obviously, finalizers has to be managed properly to avoid building a huge list of weak references, but this is another issue.

祁梦 2024-08-29 08:05:48

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

通常你不需要定义 __del__ 方法

Circular references are cleaned up automatically. There are a few exceptions, such as classes that define a __del__ method.

Usually you do not need to define a __del__ method

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文