You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

父QObject被删除后,子QObject仍可访问的原因是什么?

PyQt5父对象销毁后子对象Python包装仍存在的原因

问题场景

  • 创建QObject对象a
  • 创建以a为父对象的QObject对象b
  • 删除a

根据Qt官方文档描述:

父对象接管子对象的所有权,即会在析构时自动删除子对象。

但实际运行后出现矛盾现象:

  • 变量b对应的Python对象仍可访问,执行显式垃圾回收也未被清理
  • 调用b.parent()时会抛出RuntimeError,提示C++对象已被销毁

复现代码

import gc

from PyQt5.QtCore import QObject


def tracked_qobjects():
    return [id(o) for o in gc.get_objects() if isinstance(o, QObject)]


def children(qobject: QObject):
    return [id(c) for c in qobject.findChildren(QObject)]


a = QObject()
b = QObject(parent=a)
print(f"垃圾回收器追踪的QObjects:{tracked_qobjects()}")
# 输出:垃圾回收器追踪的QObjects:[140325587978704, 140325587978848]
print(f"a的子对象:{children(a)}")
# 输出:a的子对象:[140325587978848]

del a
print(f"垃圾回收器追踪的QObjects:{tracked_qobjects()}")
# 输出:垃圾回收器追踪的QObjects:[140325587978848]

gc.collect()  # 不保证能清理但不会有危害
print(f"垃圾回收器追踪的QObjects:{tracked_qobjects()}")
# 输出:垃圾回收器追踪的QObjects:[140325587978848]

# 预期b指向已不存在的对象,但实际仍可访问
print(id(b))
# 输出:140325587978848
print(b)
# 输出:<PyQt5.QtCore.QObject object at 0x7fa018d30a60>

# 访问父对象触发预期错误
print(b.parent())
# 抛出:RuntimeError: wrapped C/C++ object of type QObject has been deleted

原因解析

这是PyQt的双层对象模型导致的差异:

  1. 底层C++ Qt对象:完全遵循Qt的父子所有权规则,当父对象a被销毁时,子对象b的C++实例已经被自动删除。
  2. 上层Python包装对象:由Python垃圾回收器独立管理,只有当没有任何Python变量引用它时,才会被回收。这里变量b仍然持有对Python包装对象的引用,所以GC不会清理它。
  • 打印b或获取id(b)仅操作Python包装对象本身,不需要访问底层C++对象,因此不会报错。
  • 调用b.parent()这类方法时,需要访问已销毁的C++对象,因此触发RuntimeError。

内容的提问来源于stack exchange,提问作者bugmenot123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 15:07:32