在【python】详解类class的继承、init初始化、super方法(五)详见链接一文中通过super的方法使得子类调用父类的属性或函数方法。正是因为class有方法解析顺序MRO。此文将详解MRO运行逻辑。
- Python的多继承类是通过MRO的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数只调用一次(如果每个类都使用super)
- 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次
假设现在有一个如下的继承结构,首先通过类名显示调用的方式来调用父类的初始化函数:
#父类A,调用时先print:Enter A,再Leave A
class A(object):
def __init__(self):
print( " ->Enter A")
print( " <-Leave A" )
#B类继承A类,B的初始化,先print:Enter B;
#再调用A父类的初始化,运行A父类初始化打印E: A,L: A;
#最后L: B。
class B(A):
def __init__(self):
print( " -->Enter B")
A.__init__(self)
print (" <--Leave B")
#C类继承A类,C的初始化,先print:Enter C;
#再调用A父类的初始化,运行A父类初始化打印E: A,L: A;
#最后L: C。
class C(A):
def __init__(self):
print (" --->Enter C")
A.__init__(self)
print (" <---Leave C")
#D类继承B、C类,先初始化进入D,print:Enter D
#先调用B类进行初始化,运行B类的初始化
#再调用C类进行初始化,运行C类的初始化
#最后打印:Leave D
class D(B, C):
def __init__(self):
print ("---->Enter D")
B.__init__(self)
C.__init__(self)
print( "<----Leave D")
d = D()
输出结果:
#D类继承B、C类,先初始化进入D,print:Enter D
---->Enter D
#先调用B类进行初始化,运行B类的初始化
-->Enter B
->Enter A
<-Leave A
<--Leave B
#再调用C类进行初始化,运行C类的初始化
--->Enter C
->Enter A
<-Leave A
<---Leave C
#最后打印:Leave D
<----Leave D
从输出中可以看到,类A的初始化函数被调用了两次,这不是我们想要的结果;我们通过super方式来调用父类的初始化函数:
class A(object):
def __init__(self):
print( " ->Enter A")
print( " <-Leave A" )
class B(A):
def __init__(self):
print( " -->Enter B")
super(B, self).__init__()
print (" <--Leave B")
class C(A):
def __init__(self):
print (" --->Enter C")
super(C, self).__init__()
print (" <---Leave C")
class D(B, C):
def __init__(self):
print ("---->Enter D")
super(D, self).__init__()
print( "<----Leave D")
d = D()
输出结果:
---->Enter D
-->Enter B
--->Enter C
->Enter A
<-Leave A
<---Leave C
<--Leave B
<----Leave D
通过输出可以看到,当使用super后,A的初始化函数只能调用了一次。这是由于Python的类有一个_ _mro _ 属性,这个属性中就保存着方法解析顺序。结合上面的例子来看看类D的 _ mro_ _:
print("MRO:", [x.__name__ for x in D.__mro__])
输出结果
MRO: ['D', 'B', 'C', 'A', 'object']