PyQT5程序中的Garbage-Collection Mechanism与正常情况有什么区别? (如果这是 GC 相关的问题)
What is the difference between the Garbage-Collection Mechanism in PyQT5 programs and normal conditions? (If this is a GC-related problem)
为了了解PyQT5中metaclass QObject的Object-Deletion Features,我写了如下代码:
from PyQt5.Qt import *
class Window(QWidget):
def __init__(self):
super().__init__()
self.setup_UI()
self.setup_subUI()
self.hook = None
def setup_UI(self):
self.setWindowTitle('MyWindowTitle')
self.resize(600, 400)
def setup_subUI(self):
obj1 = QObject()
obj2 = QObject(obj1)
obj3 = QObject(obj2)
self.hook = obj1 # This is line 19.
obj1.setObjectName('obj1')
obj2.setObjectName('obj2')
obj3.setObjectName('obj3')
obj1.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
obj2.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
obj3.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
上面的代码有几个显而易见的事实:
Window
class 有一个非必需的属性 hook
,最初用于绑定到 obj1
;
obj1
是obj2
的父对象,obj2
是obj3
的父对象。如果没有对象参与引用计数,它们三个都会被快速恢复以激活下面的destroyed
信号接收器并进一步打印此消息。
为了避免 GC,我添加了第 19 行以尝试保留它们,self.hook = obj1
,但适得其反。
谜题如下:
如果第19行改成self.other_attribute_not_schedule = obj1
,程序在Window UI实体关闭前不会释放这3个对象。
更清楚地说,能否请您告诉我有关使用上述代码删除对象的更多详细信息,以及为什么当我使用另一行时它们不删除?
当一个对象的引用数达到 0 时,垃圾收集将激活。在这种情况下,如果 Qt 允许,python 会尝试删除它。
在您的原始代码中,您首先调用 setup_subUI
,这会创建一个引用:
self.hook = obj1
然后,在 setup_subUI
returns 之后,您执行以下操作:
self.hook = None
这导致“覆盖”self.hook
作为对 None
的引用,因此先前的引用 (obj1
) 被删除。由于那是 唯一的 参考,它连同它的子项一起被删除。
然后在 setup_subUI
中再次执行以下操作:
self.other_attribute_not_schedule = obj1
当setup_subUI
returns时,下一行会像上面这样计算:
self.hook = None
这里的区别是你没有覆盖 self.hook
因为它还不存在,并且 obj1
没有被删除,因为它的引用 (self.other_attribute_not_schedule
) 仍然存在存在。
请注意,有不同类型的引用(例如,将对象添加到持久列表)并且它们可能并不总是明确的,尤其是对于像 Qt 这样的复杂模块。
例如,将父对象添加到第一个对象的构造函数将防止删除,即使您这样做 self.hook = None
:
obj1 = QObject(self)
这是因为对象的所有权由 Qt 本身管理。
在许多情况下,Qt 会取得一个对象的所有权(如有必要,通过重新设置它的父级),例如,将一个小部件添加到布局,然后将布局设置为另一个小部件。
为了了解PyQT5中metaclass QObject的Object-Deletion Features,我写了如下代码:
from PyQt5.Qt import *
class Window(QWidget):
def __init__(self):
super().__init__()
self.setup_UI()
self.setup_subUI()
self.hook = None
def setup_UI(self):
self.setWindowTitle('MyWindowTitle')
self.resize(600, 400)
def setup_subUI(self):
obj1 = QObject()
obj2 = QObject(obj1)
obj3 = QObject(obj2)
self.hook = obj1 # This is line 19.
obj1.setObjectName('obj1')
obj2.setObjectName('obj2')
obj3.setObjectName('obj3')
obj1.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
obj2.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
obj3.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
上面的代码有几个显而易见的事实:
Window
class 有一个非必需的属性hook
,最初用于绑定到obj1
;obj1
是obj2
的父对象,obj2
是obj3
的父对象。如果没有对象参与引用计数,它们三个都会被快速恢复以激活下面的destroyed
信号接收器并进一步打印此消息。
为了避免 GC,我添加了第 19 行以尝试保留它们,self.hook = obj1
,但适得其反。
谜题如下:
如果第19行改成self.other_attribute_not_schedule = obj1
,程序在Window UI实体关闭前不会释放这3个对象。
更清楚地说,能否请您告诉我有关使用上述代码删除对象的更多详细信息,以及为什么当我使用另一行时它们不删除?
当一个对象的引用数达到 0 时,垃圾收集将激活。在这种情况下,如果 Qt 允许,python 会尝试删除它。
在您的原始代码中,您首先调用 setup_subUI
,这会创建一个引用:
self.hook = obj1
然后,在 setup_subUI
returns 之后,您执行以下操作:
self.hook = None
这导致“覆盖”self.hook
作为对 None
的引用,因此先前的引用 (obj1
) 被删除。由于那是 唯一的 参考,它连同它的子项一起被删除。
然后在 setup_subUI
中再次执行以下操作:
self.other_attribute_not_schedule = obj1
当setup_subUI
returns时,下一行会像上面这样计算:
self.hook = None
这里的区别是你没有覆盖 self.hook
因为它还不存在,并且 obj1
没有被删除,因为它的引用 (self.other_attribute_not_schedule
) 仍然存在存在。
请注意,有不同类型的引用(例如,将对象添加到持久列表)并且它们可能并不总是明确的,尤其是对于像 Qt 这样的复杂模块。
例如,将父对象添加到第一个对象的构造函数将防止删除,即使您这样做 self.hook = None
:
obj1 = QObject(self)
这是因为对象的所有权由 Qt 本身管理。
在许多情况下,Qt 会取得一个对象的所有权(如有必要,通过重新设置它的父级),例如,将一个小部件添加到布局,然后将布局设置为另一个小部件。