【问题标题】:Type error in __init__ when deriving from two metaclasses从两个元类派生时在 __init__ 中键入错误
【发布时间】:2019-03-26 16:47:02
【问题描述】:

我想创建派生自 PyQt5 QtWidget.QWidgetabc.ABCMeta 的类。这两个类都有自己的元类作为类型,因此,根据this pagethis SO question,我需要创建自己的元类,它派生自QWidgetabc.ABCMeta 的元类,并明确将其用作我的班级的元类。

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

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__ 方法来设置父级时,我得到以下TypeError:

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__ 的签名是否发生了某种变化?任何帮助将不胜感激。

【问题讨论】:

  • AbstractClass.__init__(self, name) 的目的是什么? AbstractClass 实例未保存在 ConcreteWidget 中的任何位置。
  • 在这个 MVCE 中,AbstractClass.__init__(self, name) 行确实什么都不做。在我的实际用例中,ConcreteWidget 继承自一些 ConcreteClass,而后者又继承自 AbstractClassConcreteClass__init__ 方法需要 name 参数。

标签: python pyqt pyqt5 metaclass


【解决方案1】:

QT 类旨在以协作方式在现代 Python 代码中使用 - 这意味着它们在内部使用 Python 的 super(),以确保使用各自的参数调用所有超类中的所有正确方法。

super() 的创建正是为了避免使用超类名称对超类 __init__(和其他覆盖方法)的调用进行硬编码,这样您就没有担心调用顺序。

唯一的事情是,每个打算用作多类混合的类都必须“知道”它是如何做到的,提取它使用的命名参数,然后用剩余的参数再次调用super。 QT 代码更进一步,检查其他类的所有命名参数是否都呈现给它,否则会出错。

不仅如此,由于 Qt 类本身使用 super,这意味着您在抽象类中的 __init__ 被调用了两次。它不会对这个简单的代码产生影响,但在更复杂的基类中可能会出现问题。

所以,只需重写您的 __init__ 方法,以“pythonic”方式进行:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-04
    • 2017-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-19
    • 2016-09-21
    • 2016-08-09
    相关资源
    最近更新 更多