super() 和直接调用超类的区别
Posted
技术标签:
【中文标题】super() 和直接调用超类的区别【英文标题】:Difference between super() and calling superclass directly 【发布时间】:2014-03-05 13:48:47 【问题描述】:在 Python 2.7 和 3 中,我使用以下方法调用超类的函数:
class C(B):
def __init__(self):
B.__init__(self)
我发现也可以用super(B, self).__init__()
和python3 中的super().__init__()
替换B.__init__(self)
。
这两种方式有什么优点或缺点吗?至少对我来说直接从B
调用它更有意义,但也许有充分的理由让super()
只能在使用元类时使用(我通常避免使用)。
【问题讨论】:
【参考方案1】:对于单继承,super()
只是引用基类型的一种更好的方式。这样,您可以使代码更易于维护,例如,如果您想更改基类型的名称。当你在任何地方都使用super
时,你只需要在class
行中更改它即可。
真正的好处来自多重继承。使用super
时,单次调用不仅会自动调用所有基类型的方法(按照正确的继承顺序),还会确保每个方法只调用一次。
这实质上允许类型具有diamond 属性,例如你有一个基本类型A
,还有两种类型B
和C
,它们都派生自A
。然后你有一个 D
类型,它继承自 B
和 C
(使其也隐式继承自 A
两次)。如果你现在显式调用基类型的方法,你最终会调用 A 的方法两次。但是使用super
,它只会调用一次:
class A (object):
def __init__ (self):
super().__init__()
print('A')
class B (A):
def __init__ (self):
super().__init__()
print('B')
class C (A):
def __init__ (self):
super().__init__()
print('C')
class D (C, B):
def __init__ (self):
super().__init__()
print('D')
当我们现在实例化D
时,我们得到以下输出:
>>> D()
A
B
C
D
<__main__.D object at 0x000000000371DD30>
现在,让我们再次手动调用基类型的方法:
class A2 (object):
def __init__ (self):
print('A2')
class B2 (A2):
def __init__ (self):
A2.__init__(self)
print('B2')
class C2 (A2):
def __init__ (self):
A2.__init__(self)
print('C2')
class D2 (C2, B2):
def __init__ (self):
B2.__init__(self)
C2.__init__(self)
print('D2')
这是输出:
>>> D2()
A2
B2
A2
C2
D2
<__main__.D2 object at 0x0000000003734E48>
如您所见,A2
出现了两次。这通常不是你想要的。当您手动调用使用super
的一种基本类型的方法时,它会变得更加混乱。因此,您应该只使用super()
来确保一切正常,而且您不必太担心。
【讨论】:
基本类型:?。基类、子类等 :) 看起来更自然 @MikeWilliamson 您的理解部分正确。调用super()
时,实际上不会评估确定顺序的逻辑。该顺序所依据的是Python中的方法解析顺序(MRO),它基本上是类型层次结构的一致线性化。您可以通过查看D.__mro__
来查看D
的MRO,基本上是D, C, B, A, object
。不幸的是,正确计算 MRO 有点复杂。它使用C3 linearization algorithm。但简化后,你的“看”模型是有效的。
@MikeWilliamson super()
所做的不是计算下一步,而是简单地告诉 Python 在 MRO 中继续。这就是为什么作为层次结构一部分的所有类都应该使用super()
,这就是为什么A
调用super()
。如果您要添加另一个类型class X(object)
,并使D
也继承自该类型,那么您会看到如果没有A
的super()
,X
将不会被调用。由于 X
在 MRO 中位于 A
之后,因此需要 super()
告诉 Python 在 MRO 中继续。
@MikeWilliamson 我不确定你的意思。MRO 是使用该算法计算的(这可能会变得非常复杂;查看 Wikipedia 上的示例)。一旦计算出 MRO,super()
调用将按该顺序调用类型。当然,由于super()
是函数调用,之后它以相反的顺序返回;但这只是调用堆栈。
@PiyushKansal 在类中 C
, super()
与 super(C, self)
做同样的事情。 Python 3 引入了这种快捷方式,您无需为默认情况指定参数。在大多数情况下(大约 99%),您只想使用 super()
。通过将不同的类型传递给它,例如super(ParentType, self)
,您会在 MRO 中跳过类型,所以这可能不是您想要做的。以上是关于super() 和直接调用超类的区别的主要内容,如果未能解决你的问题,请参考以下文章