python – 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创建一个闭包,其中引用了__init__方法范围内的局部self和model变量.如果在某处保留对lambda的引用,则可以通过__closure__属性检查其闭包的所有单元对象.在您的示例中,它将显示如下内容:
>>> print(func.__closure__)
(<cell at 0x7f99c16c5138: MyModel object at 0x7f99bbbf0948>, <cell at 0x7f99c16c5168: MyClass object at 0x7f99bbb81390>)
如果删除了此处显示的对MyModel和MyClass对象的所有其他引用,则单元格保留的引用仍将保留.因此,当涉及到对象清理时,您应该始终明确断开连接到可能在相关对象上形成闭包的函数的所有信号.
请注意,在信号/插槽连接方面,PyQt以不同方式处理包装的C插槽和Python实例方法.这些类型的可调用的引用计数在连接到信号时不会增加,而lambda,定义的函数,部分对象和静态方法都是.这意味着如果删除对后一类可调用的所有其他引用,则任何剩余的信号连接将使它们保持活动状态.如果需要,断开信号将允许这种连接的可调用物被垃圾收集.
上面的一个例外是类方法. PyQt在创建这些连接时创建一个特殊的包装器,因此如果删除所有其他对它们的引用,并且发出信号,则会引发异常,如下所示:
TypeError: 'managedbuffer' object is not callable
以上内容应适用于PyQt5和大多数版本的PyQt4(4.3及更高版本).
标签:python,lambda,garbage-collection,pyqt,signals-slots 来源: https://codeday.me/bug/20191002/1843695.html