Python中可以直接通过调用父类名调用父类方法,在多重继承中,使用super()是一个很好的习惯。
super的本质返回的是MRO的下一个类
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
两个参数 cls 和 inst 分别做了两件事:
这两件事才是 super 的实质,一定要记住!
MRO 全称 Method Resolution Order,它代表了类继承的顺序。
很重要的一句话:在 MRO 中,基类永远出现在派生类后面,如果有多个基类,基类的相对顺序保持不变。
放几个例子
例1:
class A:
def print(self):
print('A')
class B:
def print(self):
print('B')
class C(A):
def print(self):
print('C')
super().print()
C().print()
# C
# A
例2:
class A(object):
def __init__(self):
print('A')
class B(A):
def __init__(self):
super(B, self).__init__()
print('B')
class C(object):
def __init__(self):
print('C')
class D(C):
def __init__(self):
super(D, self).__init__()
print('D')
class E(B, D):
def __init__(self):
super(E, self).__init__()
super(A, self).__init__()
print('E')
e = E()
分析:
print(e.__class__.__mro__)得到MRO序列(
super(E, self).__init__()在上述MRO中找E后续的类为B,根据顺序找到B类继承类A,最后输出A、B
super(A, self).__init__()在MRO中找A后续的类为D,根据顺序找到D类继承类C,最后输出C、D
例3:(来自参考文献2)
class A:
def __init__(self):
print("A", end=" ")
super().__init__()
class E:
def __init__(self):
print("E", end=" ")
super().__init__()
class B(E):
def __init__(self):
print("B", end=" ")
super().__init__()
class D:
def __init__(self):
print("D", end=" ")
super().__init__()
class C(A, B, D):
def __init__(self):
print("C", end=" ")
A.__init__(self) # A B E D
D.__init__(self) # D
B.__init__(self) # B E D
print("MRO:", [x.__name__ for x in C.__mro__])
print(C())
MRO: ['C', 'A', 'B', 'E', 'D', 'object']
C A B E D D B E D <__main__.C object at 0x1040340b8>
分析:
当执行A.__init__(self)这行代码的时候,self指向了c实例对象,然后打印出A,当调用super().__init__()这行代码的时候,实际相当于super(A, self).__init__()的调用, super(A, self)的会在self.mro中查找排在A后边的类,这样就实现了继承链的调用了。
例4:(来自参考文献1)
class root(object):
def __init__(self):
print('this is root')
class B(root):
def __init__(self):
print('enter B')
super().__init__()
print('leave B')
class C(root):
def __init__(self):
print('enter C')
super().__init__()
print('leave C')
class D(B, C):
pass
d = D()
print(d.__class__.__mro__)
enter B
enter C
this is root
leave C
leave B
(, , , , )
分析:
super().__init__()这句的self还是D,所以从MRO序列中一次读取。之所以B的init会被调用是因为D中没有定义__init__,所以会在MRO中的下一个类中找到定义的__init__。
所以,使用super,如果你要改变子类继承的父类(由A改为B),在例子1中只需要修改一行代码(class C(A): -> class C(B))即可,而不需要在class C的大量代码中去查找、修改基类名,另外一方面代码的可移植性和重用性也更高。
参考文献:
1. 理解 Python super(里面提到了比较好的一篇文章)
2. python中super()的一些用法