直接用类名调用父类方法在使用单继承的时候没问题。但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题,super 是用来解决多重继承问题的。
MRO
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
MRO即method resolution order,用于判断子类调用的属性来自于哪个父类。在Python2.3之前,MRO是基于深度优先算法的,自2.3开始使用C3算法,定义类时需要继承object,这样的类称为新式类,否则为旧式类
从图中可以看出,旧式类查找属性时是深度优先搜索,新式类则是广度优先搜索。
C3算法
C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。
-
本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
-
单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。
代码1
class D(object):
def f(self):
print('这是D')
class B(D):
def f(self):
print('这是B')
def extra(self):
print('这是extra B')
class C(D):
def f(self):
print('这是C')
def extra(self):
print('这是extra C')
class A(B, C, D): # 声明顺序为B——>C——>D
def f(self):
super(A, self).f()
print('这是A')
print(A.mro()) # 打印类的方法解析顺序表
a = A()
a.f()
a.extra()
# 输出结果为:
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
这是B
这是A
这是extra B
代码2,调下类A的声明顺序
class D(object):
def f(self):
print('这是D')
class B(D):
def f(self):
print('这是B')
def extral(self):
print('这是extral B')
class C(D):
def f(self):
print('这是C')
def extral(self):
print('这是extral C')
class A(C, B, D): # 声明顺序为C——>B——>D
def f(self):
super(A, self).f()
print('这是A')
print(A.mro()) # 打印类的方法解析顺序表
a = A()
a.f()
a.extral()
# 输出结果为:
[<class '__main__.A'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.D'>, <class 'object'>]
这是C
这是A
这是extral C
小结
- super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
产生了一个super对象; - super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
- super(B, self).func的调用并不是用于调用当前类的父类的func函数;
- Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
只调用一次(如果每个类都使用super); - 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
个父类函数被调用多次。