【问题标题】:Why does super() inherits the "wrong" class? [duplicate]为什么 super() 继承了“错误”的类? [复制]
【发布时间】:2020-01-29 20:29:17
【问题描述】:

我在看钻石问题,有一个问题:


class A:
    def __init__(self):
        print("This is class A")

class B(A):
    def __init__(self):
        print("This is class B")
        super().__init__()

class C(A):
    def __init__(self):
        print("This is class C")
        super().__init__()

class D(B, C):
    def __init__(self):
        print("This is class D")
        super().__init__()


i = D()

This is class D
This is class B
This is class C
This is class A

它按预期工作,这很好,但我想知道为什么class B 中的super().__init__() 没有转到class A,而是调用了 C。

如果一个类有一个 super() 并且它继承自父类,它应该去那里。

如果我在 B 上删除它,代码将不会到达 C 或 A。

我知道 MRO 以及它实际上是如何按预期进行的:

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

但我不知道为什么。

这很奇怪,这段代码的非 super() 实现具有相同的 MRO,但 A 被打印了两次:


class A:
    def __init__(self):
        print("This is class A")

class B(A):
    def __init__(self):
        print("This is class B")
        A.__init__(self)

class C(A):
    def __init__(self):
        print("This is class C")
        A.__init__(self)

class D(B, C):
    def __init__(self):
        print("This is class D")
        B.__init__(self)
        C.__init__(self)


i = D()
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

这是相反的,我知道 MRO 是正确的,但奇怪的是实际执行不是这样:

This is class D
This is class B
This is class A
This is class C
This is class A

我想知道 super() 行为背后的逻辑是什么。

当在网上问这个问题时,几乎每个人都将我链接到这个:https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ 但我真的不明白,他的解释似乎太技术性了,他的例子(我理解的少数几个)比他们复杂得多应该是为了解释这一点......这就是为什么我想要一个......更简单的解释。

  • Super() 必须遵循 MRO,即使父类上的继承会提出其他建议?

  • Super() 无法转到父类的父类,因此如果父类中有super,它会转到第二个继承的类?

  • 另外,有点不相关,但在真实的工作环境中看到钻石问题有多普遍?似乎是一种非常复杂的工作方式。

【问题讨论】:

  • 简短的回答是D 的mro 是(D, B, C, A, object)。一定是因为BC 继承自A,所以A 必须紧随其后。
  • 1.因为这是 MRO 中的下一个实现。这就是为什么你服从超级。 2. 因为你真的把它叫了两次。再说一次,这就是你服从 super 的原因
  • "如果一个类有一个 super() 并且它继承自父类,它应该去那里。"不,不应该。那是你的根本误解。这只适用于非多重继承的简单情况。但是一般来说,super 提供了一个代理对象,它将遵循MRO 中的下一个类。这就是为什么您必须在参与协作多重继承的所有类中使用super(这就是您的第二个示例中断的原因)。
  • @OlivierMelançon 不适用。
  • super() 只是名字不好。它可能应该被称为next_class() 之类的。你不是第一个有这种困惑的人。

标签: python super diamond-problem


【解决方案1】:

您需要记住,MRO 不仅仅是一个简单的跟随领导者。它创建了一个类似图形的结构并解析了相同的继承以避免重复。在您的第一个示例中,您创建了菱形继承。

   A
  / \
 B   C
  \ /
   D

MRO 从第一层(直接父类)开始寻找方法的解析,从左到右(B 然后C),然后是下一层,从左到右(只是A这里),等等。

在这种情况下,因为 BC 都继承自 A,所以顶层解析为单个 A 并创建上面的菱形图。

让我们看看你的第二个例子:

class D(B, C):
    def __init__(self):
        print("This is class D")
        B.__init__(self)
        C.__init__(self)

通过这种方式实施,您实际上绕过了 MRO。您已将继承钻石制作成继承橄榄叉,如下所示:

 A   A
 |   |
 B   C
  \ /
   D

因此,您调用了两次A 的初始化,这不需要发生。在长继承链或复杂的初始化例程中,这是非常低效的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 2011-04-09
    • 2012-03-09
    • 2012-05-17
    • 2018-11-08
    • 2013-02-16
    • 1970-01-01
    相关资源
    最近更新 更多