【问题标题】:How to call "overriden" methods from both parent classes?如何从两个父类调用“覆盖”方法?
【发布时间】:2020-07-22 18:27:33
【问题描述】:

考虑以下类,其中P1P2C 的父类:

class P1:
    def f(self):
        print('P1')


class P2:
    def f(self):
        print('P2')


class C(P1, P2):
    def f(self):
        print('C')
        # super().f()
        # super(P1, self).f()

c = C()
c.f()

当我运行它时,它会打印出C

如果我取消注释第一行 super().f(),那么它也会打印 P1
因为super() 会调用直接父级P1 的方法

如果我取消注释第二行super(P1, self).f(),那么它也会打印P2 因为super(P1, self) 会调用P1 的兄弟P2 的方法

我想知道的是,如果有任何方法可以从父类P1P2 调用f 方法,只需调用super() 函数,而不是调用它两次我做到了。

或者,有没有其他方法可以在不使用super 函数的情况下做到这一点?

【问题讨论】:

  • 没有语言功能可以做到;这是一个不寻常的愿望。对于您确实想在所有父类中调用重写方法的极少数情况,最好明确地这样做:它更具可读性,并且行为(例如排序)将被明确定义。
  • @jamesdlin 感谢您的反馈。我只是好奇而已。这不是我“想要”的。

标签: python python-3.x overriding multiple-inheritance super


【解决方案1】:

没有什么好方法可以完全按照您的意愿行事。但是如果P1P2都可以修改,就可以实现协同多继承,只需要在两个父类中添加一个基类就可以了:

class GP: # grandparent base class
    def f(self):
        pass  # do nothing

class P1(GP):
    def f(self):
        print("P1")
        super().f()

class P2(GP):
    def f(self):
        print("P2")
        super().f()

class C(P1, P2):
    def f(self):
        print("C")
        super().f()

这是有效的,因为super 并不完全意味着“我的父类”。这意味着“此对象的 MRO 中的下一个”。 MRO 是方法解析顺序,基本上是在继承中搜索事物的顺序。当在C 中调用super() 时,它会在P1 中找到方法。但是当在P1(在C 的实例上)中调用super() 时,它会调用P2,因为C 实例的MRO 是[C, P1, P2, GP, object]P2P1 之后,所以它被第二个 super 调用选中。

需要GP 类来结束super 调用链。如果你没有它,最后一个super 调用将解析为object(它是所有继承树的根),并且由于没有这样的方法,你会得到一个错误。基类中的实现不需要什么都不做,只需要最后不调用super即可。

【讨论】:

  • 看起来它可以在没有 GP 类的情况下工作。
  • @oldwooki 如果您删除 GP 基类,那么您会在我的答案中遇到问题,即 P2 调用 object.f,从而导致 AttributeError。删除 P2 中的调用意味着如果您将 P2 更早地放入 MRO,您将不会调用 P1。
  • @steviestickman 我明白你在说什么,但让我换个说法,当我尝试时,它在没有 GP 类的情况下工作。它打印了CP1、P2`,而没有引发AttributeError
【解决方案2】:

Super根据派生类的方法解析顺序(MRO)调用下一个方法。

方法f的每一个实现都应该调用super,父类之间不需要相互了解,super会自动调用MRO中的下一个方法。

编辑:我忘记了 mro 中的最后一个类总是对象。 对象没有称为 f 的方法。因此,您应该注意 mro 中具有该方法的最后一个类,要么不调用 super().f,要么捕获 AttributeError。

只要遵循C3线性化规则,子类就可以改变MRO。这意味着派生类决定了哪些代码可以运行,哪些代码不能运行。这是依赖注入的一种方式。

您可以通过 __mro__ 属性检查类的 MRO。

这个答案主要基于 Raymond Hettinger 的谈话 super considered super

class P1:
    def f(self):
        super().f()
        print('P1')

class P2:
    def f(self):
        print('P2')

class C(P1, P2):
    def f(self):
        super().f()
        print('C')

class C2(P2, P1):
    def f(self):
        super().f()
        print('C2')

>>> C().f()
P2
P1
C
>>> C2().f()
P2
C2
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.P1'>, <class '__main__.P2'>, <type 'object'>)
>>> C2.__mro__
(<class '__main__.C2'>, <class '__main__.P2'>, <class '__main__.P1'>, <type 'object'>)

【讨论】:

  • 看起来代码在没有 super 调用 P2 的情况下工作。
  • 我无法重现您在此处显示的输出。当我调用C().f() 时,我得到一个AttributeError,当它试图找到object.f(显然不存在)。
  • @oldwooki:如果没有在P2 中调用super,您就不能以不同的顺序继承。如果你声明了class C2(P2, P1),它就不起作用了。
  • @Blckknght 我忘记了对象是基类。我已经更新了我的答案。
  • @oldwooki 您可以在 P2 中省略 super 但是当您更改 MRO 时不会调用 C1.f。请参阅我更新的答案。
猜你喜欢
  • 2014-05-11
  • 2017-11-11
  • 1970-01-01
  • 1970-01-01
  • 2011-04-17
  • 2020-11-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多