pyqt 类 的方法解析顺序是什么?
What's the method resolution order with pyqt classes?
我最近被问到有关 pyqt 继承的问题,问题是:假设我有 class A 和方法 closeEvent(event)
,以及 class B 继承 A 和QMainWindow(因此具有 closeEvent(event)
方法)。 closeEvent
的解决顺序是什么?考虑到 python 文档,这取决于继承顺序。但是下面的例子表明情况并非如此......有人可以提供帮助吗?
我在 windows 上使用 Python3.6.2 和 PyQt5.
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys
class A():
def closeEvent(self,event):
print("A")
class B(QMainWindow):
def closeEvent(self,event):
print("B")
super().closeEvent(event)
class C(A,B):
pass
class D(B,A):
pass
class E(QMainWindow,A):
pass
class F(A,QMainWindow):
pass
def test(TestClass, msg):
"""Create class instance and show it. Click on cross to close."""
print(msg)
app = QApplication(sys.argv)
test = TestClass()
test.show()
app.exec()
test(C,"C(A,B)?") # >>> A
test(D,"D(B,A)?") # >>> B
test(E,"E(QMainWindow,A)?") # >>> A ??? Why ???
test(F,"F(A,QMainWindow)?") # >>> A
这是一个非常复杂的问题,涉及到 Python 和 C++ 在协作多重继承的工作方式方面的差异。所以我要在这里给出的答案更多是简要概述的方式。我认为完整的治疗对于单个 SO 答案可能是不可行的。
部分问题在于 PyQt 如何确定 class 它应该继承什么。这可以通过比较纯 Python 与 的 class 层次结构的 PyQt 版本的 __base__
属性的值来说明:
class A: pass
class B(QMainWindow): pass
class C(A, B): pass
class D(B, A): pass
class E(QMainWindow, A): pass
class F(A, QMainWindow): pass
print('pyqt:')
print('C:', C.__base__)
print('D:', D.__base__)
print('E:', E.__base__)
print('F:', F.__base__)
class M: pass
class A: pass
class B(M): pass
class C(A, B): pass
class D(B, A): pass
class E(M, A): pass
class F(A, M): pass
print('python:')
print('C:', C.__base__)
print('D:', D.__base__)
print('E:', E.__base__)
print('F:', F.__base__)
输出:
pyqt:
C: <class '__main__.B'>
D: <class '__main__.B'>
E: <class 'PyQt5.QtWidgets.QMainWindow'>
F: <class 'PyQt5.QtWidgets.QMainWindow'>
python:
C: <class '__main__.A'>
D: <class '__main__.B'>
E: <class '__main__.M'>
F: <class '__main__.A'>
这不是错误。这里的差异完全是由于实现细节,我不打算解释(即使我可以)。 PyQt 很可能会 更改其实现以更紧密地与 Python 行为保持一致。但它选择了妥协,而不是与 Qt/C++ 中的工作方式相去甚远。我猜想这个决定是受到向后兼容性问题的影响,也许还有维护代码库的便利性。如果你想在这一点上得到明确的答案,可能你必须问 PyQt 的作者。
问题的另一个(更大的)部分在于当层次结构中的某些 classes 本质上是不合作的(例如,因为它们是用 C++ 编写的,如 Qt是)。我认为或许可以在某种程度上解决此类问题——PyQt 至少已经为 class 构造做到了这一点(参见 Support for Cooperative Multi-inheritance)。但是对于像 Qt 这样的大型库来说,这样做显然是一项更大的任务。我想总会有一些皱纹(比如你发现的那个)永远不会完全消除。
我最近被问到有关 pyqt 继承的问题,问题是:假设我有 class A 和方法 closeEvent(event)
,以及 class B 继承 A 和QMainWindow(因此具有 closeEvent(event)
方法)。 closeEvent
的解决顺序是什么?考虑到 python 文档,这取决于继承顺序。但是下面的例子表明情况并非如此......有人可以提供帮助吗?
我在 windows 上使用 Python3.6.2 和 PyQt5.
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys
class A():
def closeEvent(self,event):
print("A")
class B(QMainWindow):
def closeEvent(self,event):
print("B")
super().closeEvent(event)
class C(A,B):
pass
class D(B,A):
pass
class E(QMainWindow,A):
pass
class F(A,QMainWindow):
pass
def test(TestClass, msg):
"""Create class instance and show it. Click on cross to close."""
print(msg)
app = QApplication(sys.argv)
test = TestClass()
test.show()
app.exec()
test(C,"C(A,B)?") # >>> A
test(D,"D(B,A)?") # >>> B
test(E,"E(QMainWindow,A)?") # >>> A ??? Why ???
test(F,"F(A,QMainWindow)?") # >>> A
这是一个非常复杂的问题,涉及到 Python 和 C++ 在协作多重继承的工作方式方面的差异。所以我要在这里给出的答案更多是简要概述的方式。我认为完整的治疗对于单个 SO 答案可能是不可行的。
部分问题在于 PyQt 如何确定 class 它应该继承什么。这可以通过比较纯 Python 与 的 class 层次结构的 PyQt 版本的 __base__
属性的值来说明:
class A: pass
class B(QMainWindow): pass
class C(A, B): pass
class D(B, A): pass
class E(QMainWindow, A): pass
class F(A, QMainWindow): pass
print('pyqt:')
print('C:', C.__base__)
print('D:', D.__base__)
print('E:', E.__base__)
print('F:', F.__base__)
class M: pass
class A: pass
class B(M): pass
class C(A, B): pass
class D(B, A): pass
class E(M, A): pass
class F(A, M): pass
print('python:')
print('C:', C.__base__)
print('D:', D.__base__)
print('E:', E.__base__)
print('F:', F.__base__)
输出:
pyqt:
C: <class '__main__.B'>
D: <class '__main__.B'>
E: <class 'PyQt5.QtWidgets.QMainWindow'>
F: <class 'PyQt5.QtWidgets.QMainWindow'>
python:
C: <class '__main__.A'>
D: <class '__main__.B'>
E: <class '__main__.M'>
F: <class '__main__.A'>
这不是错误。这里的差异完全是由于实现细节,我不打算解释(即使我可以)。 PyQt 很可能会 更改其实现以更紧密地与 Python 行为保持一致。但它选择了妥协,而不是与 Qt/C++ 中的工作方式相去甚远。我猜想这个决定是受到向后兼容性问题的影响,也许还有维护代码库的便利性。如果你想在这一点上得到明确的答案,可能你必须问 PyQt 的作者。
问题的另一个(更大的)部分在于当层次结构中的某些 classes 本质上是不合作的(例如,因为它们是用 C++ 编写的,如 Qt是)。我认为或许可以在某种程度上解决此类问题——PyQt 至少已经为 class 构造做到了这一点(参见 Support for Cooperative Multi-inheritance)。但是对于像 Qt 这样的大型库来说,这样做显然是一项更大的任务。我想总会有一些皱纹(比如你发现的那个)永远不会完全消除。