【问题标题】:The real solution for multiple inheritance with different init parameters不同init参数多重继承的真正解决方案
【发布时间】:2017-06-10 17:13:37
【问题描述】:

这似乎是一项简单的任务,但我现在在这方面花费了太多时间,但没有解决方案。这是设置:

class A(object):
  def __init__(self, x=0):
    print("A.__init__(x=%d)" % x)

class B(object):
  def __init__(self, y=1):
    print("B.__init__(y=%d)" % y)

class C(A, B):
  def __init__(self, x=2, y=3, z=4):
    super().__init__(x=x, y=y)
    print("C.__init__(z=%d)" % z)

是这样的想法,但这当然会导致

TypeError: __init__() got an unexpected keyword argument 'y'

所有其他尝试都以类似的方式失败,我可以在互联网上找到 no 资源并提供正确的解决方案。唯一的解决方案包括用*args, **kwargs 替换所有初始化参数。这并不真正适合我的需要。


根据要求,一个真实的例子:
(这使用了一种不同的方法,它具有有效的语法,但会产生不需要的结果。)

from PyQt5.QtCore import QObject

class Settings(object):
    def __init__(self, file):
        self.file = file

class SettingsObject(object):
    def __init__(self, settings=None):
        print("Super Init", settings is None)
        self.settings = settings

class MyObject(QObject, SettingsObject):
    def __init__(self, param):
        print("Entering Init")
        QObject.__init__(self)
        SettingsObject.__init__(self, settings=Settings(__file__))
        self.param = param
        print("Leaving Init")

结果:

Entering Init
Super Init True
Super Init False
Leaving Init

我希望 Super Init True 行消失。

【问题讨论】:

  • super 不调用 all 超类方法,只调用 下一个 方法。您目前无法正确设置 C,因为它继承自具有不兼容接口的类。
  • 那么没有办法从两个具有不同初始化签名的类继承?
  • 除非层次结构中的所有类都正确使用*args**kwargs,因此它们至少兼容
  • 你能举一个不那么抽象的例子吗?完全不清楚为什么这令人惊讶(或“令人失望”)。您当然不应该自己调用层次结构中的所有其他方法,这就是super for 的用途。我也建议阅读 Liskov 替换原则。
  • 不清楚的是你为什么这样做。你想在上面存储什么设置,为什么你决定使用多重继承来做到这一点?给出一些实际的背景——你真正想要实现的目标是什么,你为什么首先开始这样做?

标签: python python-3.x initialization multiple-inheritance


【解决方案1】:

您似乎对一些事情感到困惑,所以:

  1. 为什么会得到TypeError: __init__() got an unexpected keyword argument 'y'

    因为__init__在方法解析顺序(MRO)中的下一个实现是A.__init__,它只接受x。 MRO 是 C -> A -> B,因此您必须让 A.__init__ 接受 y(具体或使用 **kwargs)并将其传递(再次使用 super)给 @ 987654332@。

    super 不会调用 every 实现,当然也不能确保它们都只得到他们期望的参数,它只是调用 next 实现包含它给出的所有参数。

  2. 为什么SettingsObject.__init__ 会被调用两次,一次有settings,一次没有?

    似乎QObject.__init__ 包含对super().__init__ 的调用,它调用SettingsObject.__init__,因为这是MyObject 的MRO 中的下一个。但是它不传递任何settings 参数,所以第一次调用它时你会看到settings is None。第二次你直接调用它,并明确传递settings,所以你看到settings is not None

  3. 你如何编写混合类?

    我认为这是您真正应该问的问题。 SettingsObject 应该是一个混合类,因此被正确设计为与它所混合的层次结构中的其他类协作。在这种情况下:

    class SettingsObject:  # (object) isn't needed in 3.x
        def __init__(self, *args, settings=None, **kwargs):
            super().__init__(*args, **kwargs)
            self.settings = settings
    

    从您的示例看来,QObject.__init__ 没有任何必需的参数,但您仍然应该编写混入以在可选参数的情况下发挥良好作用或在其他地方重用。那么MyObject 的实现看起来像:

    class MyObject(SettingsObject, QObject):
    
        def __init__(self, param):
            super().__init__(settings=Settings(file))
            self.param = param
    

    请注意:

    • 混合类在子类化时首先出现,所以MRO是MyObject -> SettingsObject -> QObject;和
    • 您调用一次super().__init__,而不是单独调用每个超类实现。

作为替代方案,您是否考虑过作曲?也许MyObject 应该采取设置:

 class MyObject(QObject):

     def __init__(self, param, settings=None):
         super().__init__()
         self.param = param
         self.settings = settings

obj = MyObject('something', Settings(__file__))

现在您调用self.settings.method(...) 而不是self.method(...),但您没有多重继承引入的复杂性。

【讨论】:

  • 我很感激为这个答案付出的努力。我不知道 mixins,也不认为它们是我问题的答案,但它们当然是。我真的觉得应该更容易找到这个,我学得太晚了,我不得不基本上重写了我项目中的所有__init__方法,总共改变了大约400行代码。我仍然不明白为什么我的问题被否决了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-09
  • 2012-08-14
  • 1970-01-01
  • 2012-11-15
  • 2023-04-08
  • 1970-01-01
相关资源
最近更新 更多