关于python:从两个元类派生时在__init__中输入错误

Type error in __init__ when deriving from two metaclasses

我想创建一个从pyqt5 QtWidget.QWidgetabc.ABCMeta派生的类。这两个类都有自己的元类作为类型,因此,根据这个页面和这个问题,我需要创建自己的元类,它派生自QWidgetabc.ABCMeta的元类,并显式地将其用作类的元类。

到目前为止,我已经定义了一个QtAbcMeta类,并将它用作我的ConcreteWidget类的metaclass(见下文)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import abc
from PyQt5 import QtWidgets, QtCore


class AbstractClass(metaclass=abc.ABCMeta):

    def __init__(self, name):
        self._name = name

    @abc.abstractmethod
    def myMethod():
        pass


class QtAbcMeta(type(QtWidgets.QWidget), type(AbstractClass)):
    pass


class ConcreteWidget(QtWidgets.QWidget, AbstractClass, metaclass=QtAbcMeta):

    def __init__(self, name, parent=None):
        AbstractClass.__init__(self, name)
        QtWidgets.QWidget.__init__(self, parent=parent)  # This gives a type error.


    def myMethod():
        print("My widget does something")


def main():
    app = QtWidgets.QApplication([])
    myWidget = ConcreteWidget('my name', parent=None)

if __name__ =="__main__":
    main()

但是,当我试图调用QtWidgets.QWidget方法的__init__方法来设置父级时,我得到以下类型错误:

1
2
3
4
5
6
7
8
Traceback (most recent call last):
  File"main.py", line 36, in <module>
    main()
  File"main.py", line 33, in main
    myWidget = ConcreteWidget('my name', parent=None)
  File"main.py", line 24, in __init__
    QtWidgets.QWidget.__init__(self, parent=parent)  # This gives a type error.
TypeError: __init__() missing 1 required positional argument: 'name'

我不知道这里出了什么事。QtWidgets.QWidget.__init__的签名是否有变化?任何帮助都将不胜感激。


Qt类被设计成以一种合作的方式在现代的Python代码中使用——这意味着它们在内部使用了Python的super(),以确保用各自的参数调用所有超类中的所有正确方法。

创建super()完全是为了避免使用超级类名称对超级类__init__和其他重写方法进行硬代码调用,这样就不必担心调用顺序。

唯一的一点是,要用作多类混合的每个类都必须"知道"它是如何使用的,提取它使用的命名参数,然后用剩余的参数再次调用super。qt代码更进一步,检查是否向它提供了其他类的所有命名参数,否则它会出错。

更重要的是,由于qt类本身使用super,这意味着抽象类中的__init__被调用了两次。在这个简单的代码中,它不会有什么不同,但在更复杂的基类中,它可能是一个问题。

所以,只需重写你的__init__方法,以"Python式"的方式来完成它:

1
2
3
4
class ConcreteWidget(QtWidgets.QWidget, AbstractClass, metaclass=QtAbcMeta):

    def __init__(self, name, parent=None):
        super().__init__(name=name, parent=parent)

根据文档,您必须将所有参数传递给qwidget,尽管您不需要它:

1
2
3
4
class ConcreteWidget(QtWidgets.QWidget, AbstractClass, metaclass=QtAbcMeta):
    def __init__(self, name, parent=None):
        AbstractClass.__init__(self, name)
        QtWidgets.QWidget.__init__(self, name=name, parent=parent)