由于Python是允许多继承的,使用旧的Base.__init__()方式调用基类初始化方法时,在钻石继承的情况下就有可能出现钻石顶的基类函数被调用多次。因此Python2引入了super(A, self).__init__(),等价的python3中则使用super().xx()。虽然super常见于类的__init__中调用父类__init__进行父类相关初始化,但也可以用于调用其他基类函数。super是一个类而不是函数。
super正确运行依赖于MRO机制。MRO,方法解析顺序,是Python用来确定多继承时方法调用时基类查找顺序的机制。Python使用C3算法把类的关系转化为一个列表。这个类列表的MRO存储__mro__中,通过直接访问__mro__或者mro()可以查看。例如以下钻石继承例子:
class A(object): def __init__(self): print('init A') def show(self): print('show A') class B(A): def __init__(self): super(B, self).__init__() print('init B') def show(self): print('show B') class C(A): def __init__(self): super(C, self).__init__() print('init C') class D(B, C): def __init__(self): super(D, self).__init__() print('init D')
各类的MRO分别为:
(<class '__main__.A'>, <type 'object'>) (<class '__main__.B'>, <class '__main__.A'>, <type 'object'>) (<class '__main__.C'>, <class '__main__.A'>, <type 'object'>) (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
假设新建D的实例,d = D(),则输出为
init A init C init B init D
刚好与D的mro列表相反,且A只被调用一次。为什么?
super(X,self)实际上返回的是self所属类的mro列表中位于当前类X之后的类。逻辑类似以下(super是个类)
def super(cls, inst): mro = inst.__class__.mro() return mro[mro.index(cls) + 1]
以super(D, self).__init__()为例,cls为D, inst为self,即d,inst.__class__为D,mro为[D, B, C, A],因此实际执行B.__init__()。
而B.__init__()执行了super(B, self).__init__(),cls为B, inst为self,即d,inst.__class__为D,mro为[D, B, C, A],因此实际执行C.__init__()。
最后A.__init__()被执行。
确切的说super指的不是父类,而是 MRO 中的下一个类!
MRO的具体计算参考https://www.python.org/download/releases/2.3/mro/或者https://rhettinger.wordpress.com/2011/05/26/super-considered-super/。
其他:
https://laike9m.com/blog/li-jie-python-super,70/
The post python super原理 appeared first on wingyiu.