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,还有两种类型BC,它们都派生自A。然后你有一个 D 类型,它继承自 BC (使其也隐式继承自 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 也继承自该类型,那么您会看到如果没有Asuper()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() 和直接调用超类的区别的主要内容,如果未能解决你的问题,请参考以下文章

super

在 Python 中,为啥为没有定义超类的类调用 super() 函数不是错误? [复制]

super和this的区别

从超类的指针调用时,基类函数不会覆盖超类函数

super调用其他类的方法

8- 类