super()的用法要分为两种情况:在单类继承中的用法、在多类继承中的用法。下文将分别讲解:

super()在单类继承中的用法:

单类继承中,其意义就是不需要父类的名称来调用父类的函数,因此当子类改为继承其他父类的时候,不需要对子类内部的父类调用函数做任何修改就能调用新父类的方法。此外,在子类方法与父类方法重名时,在子类中通过super().xxxx可调用父类的xxxx方法( 当然,也可以使用“父类.xxxx(self)”来调用重名的父类的方法,但是这样就必须知道父类的名称,还得传入一个参数:self )

一个典型的应用场景就是子类想调用父类的__init__方法时:

img

上面代码想要表达的是:

如果子类(Uncle)继承父类(Person)不做初始化,那么会自动继承父类(Person)属性name。
如果子类(Uncle_Init)继承父类(Person)做了初始化,且不调用super初始化父类构造函数,那么子类(Uncle_Init)不会自动继承父类的属性(name)。
如果子类(Uncle_super)继承父类(Person)做了初始化,且调用了super初始化了父类的构造函数,那么子类(Uncle_Super)也会继承父类的(name)属性。

super()在多类继承中的用法

super()在多类继承中的作用体现在MRO列表上。

何谓MRO列表:对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表它代表了类继承的顺序,我们可以使用“类.mro()”获得这个列表。

那它有什么用呢?

来看看有这样的一个继承关系的类(钻石继承):

1
2
3
4
5
6
7
  Base
/ \
/ \
A B
\ /
\ /
C

代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Base:
def __init__(self):
print('Base.__init__')

class A(Base):
def __init__(self):
Base.__init__(self)
print('A.__init__')


class B(Base):
def __init__(self):
Base.__init__(self)
print('B.__init__')


class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print('C.__init__')


C()

输出的结果是:

1
Base.__init__A.__init__Base.__init__B.__init__C.__init__

每个子类都调用父类的__init__方法,想把所有的初始化操作都做一遍,但是出现了一个问题,Base类的__init__方法被调用了两次,这是多余的操作,也是不合理的。

那我们改写成使用super()的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Base:
def __init__(self):
print('Base.__init__')

class A(Base):
def __init__(self):
super().__init__()
print('A.__init__')


class B(Base):
def __init__(self):
super().__init__()
print('B.__init__')


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


C()

输出的结果是:

1
Base.__init__B.__init__A.__init__C.__init__

这样执行的结果就比较满意,是大多数人想要的结果。那为什么会是这样的结果呢?

那是因为我们每定义一个类的时候,Python都会创建一个MRO列表,用来管理类的继承顺序。

1
2
print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]

Python通过这个列表从左到右,查找继承的信息。Python3中的类都是新式类,都有这个mro属性,能看出来是广度优先的查找原则。经典类就没有mro属性,但它的查找原则是深度优先。

最后总结一下:

单类继承时super()的作用在于:1.无需知道父类名称,即可调用父类方法,在父类名称发生修改后,也无需修改子类名称。2.调用与子类方法重名的父类方法(无需使用父类方法)

多类继承时super()的作用在于:在初始化时,确保父类的初始化方法只被执行一遍。

不错的参考资料:https://www.cnblogs.com/szy13037-5/articles/9562639.html

如有偏差,欢迎赐教!