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

如何在Python中追踪PyQt5对象的所有属性访问?重写__getattribute__未生效的问题咨询

如何在Python中追踪PyQt5对象的所有属性访问?重写__getattribute__未生效的问题咨询

你遇到的这个问题其实是PyQt的底层绑定机制在搞鬼——PyQt是Qt C库的Python封装,很多方法调用是由Qt的C框架直接触发的,根本不会走Python的__getattribute__属性查找流程,所以你的打印语句才没被触发。

至于你看到sizeHint called的输出,是因为你在Python里重写了这个方法,当Qt的C++端调用它时,会直接调用你定义的Python版本,但这个调用过程并没有经过Python的属性查找,所以__getattribute__完全没参与进来。

下面给你几个可行的解决方案,帮你追踪到所有PyQt对象的方法调用:

方案一:用sys.settrace追踪所有Python函数调用

sys.settrace可以设置一个全局追踪函数,只要是Python函数被调用(包括Qt C++端触发的Python方法),都会触发它。这个方法能覆盖绝大多数场景,是最全面的追踪方式:

import sys
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication

def trace_calls(frame, event, arg):
    if event == 'call':
        func_name = frame.f_code.co_name
        # 过滤掉一些系统内置的特殊函数,只追踪业务相关方法
        if not func_name.startswith('__') or func_name in ('__init__', '__getattribute__'):
            print(f"正在调用函数: {func_name}")
    return trace_calls

class TestWidget(QWidget):
    def __init__(self):
        super().__init__()

    def sizeHint(self):
        print('sizeHint called')
        return QSize(400, 400)

class MyApplication(QMainWindow):
    def __init__(self):
        super().__init__()
        w = TestWidget()
        self.setCentralWidget(w)
        self.show()

# 启动追踪
sys.settrace(trace_calls)
app = QApplication(sys.argv)
window = MyApplication()
sys.exit(app.exec_())

方案二:动态包装所有继承的Qt方法

利用Qt的元对象系统,我们可以获取到TestWidget继承的所有Qt方法,然后动态把它们包装成带打印逻辑的函数,这样每次调用都会触发日志:

import sys
from types import MethodType
from PyQt5.QtCore import QSize, QMetaMethod
from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication

class TestWidget(QWidget):
    def __init__(self):
        super().__init__()
        # 通过元对象获取所有继承的方法
        meta_obj = self.metaObject()
        for i in range(meta_obj.methodCount()):
            method = meta_obj.method(i)
            method_name = method.name().data().decode()
            # 跳过自定义方法和特殊方法,避免重复包装
            if method_name in dir(self) and method_name not in ['__init__', 'sizeHint']:
                original_method = getattr(self, method_name)
                # 定义包装函数
                def wrapped_method(self, *args, original=original_method, name=method_name):
                    print(f"方法被调用: {name}")
                    return original(*args)
                # 替换原方法
                setattr(self, method_name, MethodType(wrapped_method, self))

    def sizeHint(self):
        print('sizeHint called')
        return QSize(400, 400)

class MyApplication(QMainWindow):
    def __init__(self):
        super().__init__()
        w = TestWidget()
        self.setCentralWidget(w)
        self.show()

app = QApplication(sys.argv)
window = MyApplication()
sys.exit(app.exec_())

注意:有些Qt核心方法是纯C++实现的,无法在Python层面包装,可能会出现报错,你可以根据实际情况调整过滤规则。

方案三:用__getattr__捕获未定义的属性访问

如果你的目标只是追踪那些没有在Python类中定义的Qt方法,可以用__getattr__——它只会在属性找不到的时候被调用,局限性比较大,但实现简单:

import sys
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication

class TestWidget(QWidget):
    def __init__(self):
        super().__init__()

    def __getattr__(self, name):
        print(f"__getattr__ 被调用,访问的属性是: {name}")
        return super().__getattr__(name)

    def sizeHint(self):
        print('sizeHint called')
        return QSize(400, 400)

class MyApplication(QMainWindow):
    def __init__(self):
        super().__init__()
        w = TestWidget()
        self.setCentralWidget(w)
        self.show()

app = QApplication(sys.argv)
window = MyApplication()
sys.exit(app.exec_())

这个方法只能捕获那些没有被Python类覆盖的Qt方法,已经存在的属性不会触发它。

备注:内容来源于stack exchange,提问作者Louis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 13:43:07