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