【问题标题】:Difference between super() and calling superclass directlysuper() 和直接调用超类的区别
【发布时间】: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() 只能在使用元类时使用(我通常避免使用)。

【问题讨论】:

    标签: python oop python-2.7 python-3.x superclass


    【解决方案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 中跳过类型,所以这可能不是您想要做的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-17
    • 2013-02-25
    • 1970-01-01
    • 2017-05-23
    相关资源
    最近更新 更多