有什么区别:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

and:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

我看到super在只有单一继承的类中被大量使用。我可以理解为什么在多重继承中使用它,但不清楚在这种情况下使用它的优势是什么。


当前回答

有很多很棒的答案,但对于视觉学习者来说: 首先让我们来探讨与参数超,然后没有。

假设有一个从jack类创建的实例jack,它具有如图中绿色所示的继承链。调用:

超级(杰克,杰克).method(…)

将使用jack的MRO(方法解析顺序)(其继承树按一定顺序),并从jack开始搜索。为什么可以提供父类?如果我们从实例jack开始搜索,它会找到实例方法,重点是找到它的父方法。

如果不向super提供参数,就像传入的第一个参数是self类,传入的第二个参数是self。这些在Python3中是自动计算的。

然而,假设我们不想使用Jack的方法,而不是传入Jack,我们可以传入Jen,开始向上搜索Jen的方法。

它一次搜索一个层(宽度而不是深度),例如,如果Adam和Sue都有所需的方法,来自Sue的方法将首先被找到。

如果Cain和Sue都有所需的方法,则会首先调用Cain的方法。 这在代码中对应于:

Class Jen(Cain, Sue):

MRO从左到右。

其他回答

我曾经使用过super(),并且认识到我们可以改变调用顺序。

例如,我们有下一个层次结构:

    A
   / \
  B   C
   \ /
    D

在这种情况下,D的MRO将是(仅适用于Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

让我们创建一个类,其中super()在方法执行后调用。

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / ⇖
  B ⇒ C
   ⇖ /
    D

所以我们可以看到分辨率顺序和MRO是一样的。但是当我们在方法的开头调用super()时:

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

我们有一个不同的顺序,它颠倒了MRO元组的顺序。

    A
   / ⇘
  B ⇐ C
   ⇘ /
    D 

对于额外的阅读,我推荐下面的答案:

带有super的C3线性化示例(一个大层次结构) 新旧样式类之间的重要行为变化 关于新式课程的内幕

super()在单继承中的好处是微乎其微的——大多数情况下,您不必将基类的名称硬编码到每个使用其父方法的方法中。

然而,如果没有super(),几乎不可能使用多重继承。这包括常见的习惯用法,如mixin、接口、抽象类等。这将扩展到稍后扩展您的代码。如果有人后来想要编写一个扩展Child和mixin的类,他们的代码将无法正常工作。

简单来说就是Super()

每个Python实例都有一个创建它的类。 Python中的每个类都有一个祖先类链。 使用super()委托的方法工作到实例类的链中的下一个祖先。

例子

这个小例子涵盖了所有有趣的情况:

class A:
    def m(self):
        print('A')

class B(A):
    def m(self):
        print('B start')
        super().m()
        print('B end')
        
class C(A):
    def m(self):
        print('C start')
        super().m()
        print('C end')

class D(B, C):
    def m(self):
        print('D start')
        super().m()
        print('D end')

调用的确切顺序由调用该方法的实例决定:

>>> a = A()
>>> b = B()
>>> c = C()
>>> d = D()

例如a,没有super调用:

>>> a.m()
A

例如实例b,祖先链为b -> A ->对象:

>>> type(b).__mro__   
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

>>> b.m()
B start
A
B end

例如c,父链为c -> A ->对象:

>>> type(c).__mro__   
(<class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

>>> c.m()
C start
A
C end

例如,祖先链d更有趣d -> B -> C -> A ->对象(mro代表方法解析顺序):

>>> type(d).__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

>>> d.m()
D start
B start
C start
A
C end
B end
D end

更多的信息

回答了“super在Python中做什么?”的问题后,下一个问题是如何有效地使用它。请看这个循序渐进的教程或者这个45分钟的视频。

这里有一些很好的答案,但它们没有解决在层次结构中的不同类具有不同签名的情况下如何使用super()……特别是在__init__的情况下

为了回答这个问题并能够有效地使用super(),我建议阅读我的答案super()并更改合作方法的签名。

下面是这种情况的解决方案:

层次结构中的顶级类必须继承自自定义类,如SuperObject: 如果类可以接受不同的参数,始终将接收到的所有参数作为关键字参数传递给超函数,并且始终接受**kwargs。

class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

使用示例:

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

输出:

E name=python age=28
C age=28
A
D name=python
B
SuperObject

所有这些不都假定基类是一个新型的类吗?

class A:
    def __init__(self):
        print("A.__init__()")

class B(A):
    def __init__(self):
        print("B.__init__()")
        super(B, self).__init__()

将不能在python2中工作。类A必须是new-style,即:类A(对象)