super()和直接调用超类之间的区别

joh*_*aas 28 python oop superclass python-2.7 python-3.x

在Python 2.7和3中,我使用以下方法来调用超类的函数:

class C(B):
    def __init__(self):
        B.__init__(self)
Run Code Online (Sandbox Code Playgroud)

我看到也可以B.__init__(self)super(B, self).__init__()和替换python3 super().__init__().

这样做有什么优点或缺点吗?B至少直接为我调用它更有意义,但也许有一个很好的理由super()只能在使用元类时使用(我通常会避免).

pok*_*oke 52

对于单继承,super()只是引用基类型的更好的方式.这样,您可以使代码更易于维护,例如,如果您想要更改基本类型的名称.当你在super任何地方使用时,你只需要改变它class.

然而真正的好处是多重继承.使用时super,单个调用不仅会自动调用所有基类型的方法(按正确的继承顺序),而且还会确保每个方法只调用一次.

这基本上允许类型具有菱形属性,例如,您具有单个基本类型A,以及两种类型B,C并且两者都派生自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')
Run Code Online (Sandbox Code Playgroud)

当我们现在实例化时D,我们得到以下输出:

>>> D()
A
B
C
D
<__main__.D object at 0x000000000371DD30>
Run Code Online (Sandbox Code Playgroud)

现在,让我们再次手动调用基类型的方法:

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')
Run Code Online (Sandbox Code Playgroud)

这是输出:

>>> D2()
A2
B2
A2
C2
D2
<__main__.D2 object at 0x0000000003734E48>
Run Code Online (Sandbox Code Playgroud)

如您所见,A2发生两次.这通常不是你想要的.当您手动调用其中一个使用的基类型的方法时,它会变得更加混乱super.所以相反,你应该只是super()用来确保一切正常,而且你也不必担心它太多.

  • @MikeWilliamson你的理解是部分正确的.当调用`super()`时,实际上不会计算确定顺序的逻辑.命令的基础是Python中的*Method Resolution Order*(MRO),它基本上是类型层次结构的一致线性化.你可以通过查看`D .__ mro__`来看到"D"的MRO,它基本上是"D,C,B,A,对象".不幸的是,正确计算MRO*有点复杂; 它使用[C3线性化算法](https://en.wikipedia.org/wiki/C3_linearization).但简化了,你的"视觉"模型有效. (3认同)
  • 基本类型:?基类,子类等:)似乎更自然 (2认同)
  • @MikeWilliamson `super()` 所做的不是计算下一步,而是简单地告诉 Python 在 MRO 内*继续*。这就是为什么作为层次结构一部分的所有类都应该使用“super()”,这就是“A”调用“super()”的原因。如果你要添加另一个类型`class X(object)`,并使`D`也继承自它,那么你会看到如果没有`A`的`super()`,`X`就不会被调用. 由于`X` 在MRO 中出现在`A` 之后,`super()` 需要告诉Python 在MRO 内继续。 (2认同)