假设我有一个对象,并希望在发出 PyQt 信号时执行其方法之一。假设我希望它使用信号未传递的参数来执行此操作。所以我创建了一个 lambda 作为信号槽:
class MyClass(object):
def __init__(self, model):
model.model_changed_signal.connect(lambda: self.set_x(model.x(), silent=True))
现在,通常使用 PyQt 信号和槽,信号连接不会阻止垃圾收集。当连接的槽的对象被垃圾收集时,当发出相应的信号时,该槽将不再被调用。
然而,使用 lambda 时这是如何工作的呢?我不存储对 lambda 的引用,但信号槽连接确实保持工作。所以 lambda 不会被垃圾回收。
如果我现在设置实例MyClass
to None
,该实例也不会被垃圾回收:发出model_changed_signal
仍然成功执行 lambda。显然,对实例的引用MyClass
保存在某个地方(也许在 lambda 的上下文中?) - 我不想要。
为什么会发生这种情况?
The lambda
在你的例子中形成了一个闭包。也就是说,它是一个嵌套函数,引用其封闭范围内可用的对象。每个创建闭包的函数都会保留一个细胞对象 https://docs.python.org/3.6/c-api/cell.html对于每个需要维护引用的项目。
在你的例子中,lambda
创建一个引用本地的闭包self
and model
范围内的变量__init__
方法。如果您保留对lambda
在某个地方,您可以通过其检查其闭包的所有单元对象__closure__
属性。在您的示例中,它将显示如下内容:
>>> print(func.__closure__)
(<cell at 0x7f99c16c5138: MyModel object at 0x7f99bbbf0948>, <cell at 0x7f99c16c5168: MyClass object at 0x7f99bbb81390>)
如果您删除了所有其他引用MyModel
and MyClass
这里显示的物体,细胞保留的物体仍然会保留。因此,当涉及到对象清理时,您应该始终显式断开连接到可能在相关对象上形成闭包的函数的所有信号。
请注意,当涉及到信号/槽连接时,PyQt 对包装的 C++ 槽和 Python 实例方法的处理方式有所不同。这些类型的可调用的引用计数是not当它们连接到信号时会增加,而 lambda、已定义函数、部分对象和静态方法则会增加。这意味着,如果删除了对后一种可调用类型的所有其他引用,则任何剩余的信号连接将使它们保持活动状态。如有必要,断开信号连接将允许对此类连接的可调用对象进行垃圾收集。
上述的一个例外是类方法。 PyQt 在创建与这些连接的连接时会创建一个特殊的包装器,因此如果删除对它们的所有其他引用,并且发出信号,则会引发异常,如下所示:
TypeError: 'managedbuffer' object is not callable
上述内容应适用于 PyQt5 和 PyQt4 的大多数版本(4.3 及更高版本)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)