lambda中对象的生命周期连接到pyqtSignal

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lambda中对象的生命周期连接到pyqtSignal相关的知识,希望对你有一定的参考价值。

假设我有一个对象,并希望在发出PyQt信号时执行其中一个方法。并且假设我希望它使用未通过信号传递的参数来执行此操作。所以我创建一个lambda作为信号的槽:

class MyClass(object):
    def __init__(self, model):
        model.model_changed_signal.connect(lambda: self.set_x(model.x(), silent=True))

现在,通常使用PyQt信号和插槽,信号连接不会阻止垃圾收集。当连接的插槽的对象被垃圾收集时,在发出相应的信号时将不再调用插槽。

但是,使用lambdas时这是如何工作的?我不存储对lambda的引用,但信号槽连接确实保持工作。所以lambda不是垃圾收集的。

如果我现在将MyClass的实例设置为None,那么该实例也不会被垃圾收集:发出model_changed_signal仍然成功执行lambda。显然,对MyClass实例的引用保留在某处(也许在lambda的上下文中?) - 这是我不想要的。

为什么会这样?

答案

你的例子中的lambda形成了一个闭包。也就是说,它是一个嵌套函数,它引用其封闭范围内可用的对象。每个创建闭包的函数都会为维护引用所需的每个项保留cell object

在你的例子中,lambda创建了一个闭包,引用了self方法范围内的局部model__init__变量。如果在某处保留对lambda的引用,则可以通过其__closure__属性检查其闭包的所有单元对象。在您的示例中,它将显示如下内容:

>>> print(func.__closure__)
(<cell at 0x7f99c16c5138: MyModel object at 0x7f99bbbf0948>, <cell at 0x7f99c16c5168: MyClass object at 0x7f99bbb81390>)

如果删除了对此处显示的MyModelMyClass对象的所有其他引用,则单元格保留的对象仍将保留。因此,当涉及到对象清理时,您应该始终明确断开连接到可能在相关对象上形成闭包的函数的所有信号。


请注意,在信号/插槽连接方面,PyQt以不同方式处理包装的C ++插槽和Python实例方法。这些类型的可调用的引用计数在连接到信号时不会增加,而lambda,定义的函数,部分对象和静态方法都是。这意味着如果删除对后一类可调用的所有其他引用,则任何剩余的信号连接将使它们保持活动状态。如果需要,断开信号将允许这种连接的可调用物被垃圾收集。

上面的一个例外是类方法。 PyQt在创建这些连接时创建一个特殊的包装器,因此如果删除所有其他对它们的引用,并且发出信号,则会引发异常,如下所示:

TypeError: 'managedbuffer' object is not callable

以上内容应适用于PyQt5和大多数版本的PyQt4(4.3及更高版本)。

以上是关于lambda中对象的生命周期连接到pyqtSignal的主要内容,如果未能解决你的问题,请参考以下文章

从字典创建 pyqtSignals

C++ lambda 表达式的生命周期是多少?

Derby/JDBC 连接生命周期(或空闲超时)

这个对象生命周期扩展闭包是 C# 编译器错误吗?

信号未连接到方法

Django请求生命周期