MRO
MRO即Method Resolution Order(方法解析顺序),即在调用方法时,会对当前类以及所有的基类进行一个搜索,以确定该方法之所在,而这个搜索的顺序就是MRO
历史原因
Python2.3之前,MRO的实现是基于DFS的,而在Python2.3以后MRO的实现是基于C3算法
C3算法
C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题
- 本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类(假如C被其他类继承了,而且C重写了A,则要按照重写后的来,而不是C重写的应用不了)
- 单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序(通俗理解:像继承遗产一样,如果继承人的儿子媳妇不在了,要先找继承人的孙子,孙女啊,而不是找什么侄子,外甥什么的)
MRO 列表就是合并所有父类的 MRO 列表,遵循的原则如下
-
子类永远在父类前面
-
如果有多个父类,会根据它们在列表中的顺序被检查
-
如果对下一个类存在两个合法的选择,选择第一个父类
通过.mro()查看
- 正常继承:如果两个互不相关的类多重继承,此时MRO正常,不会引起任何问题;
- 菱形继承:B和C有公共父类D,假设C重写了D中的fun()方法,按照MRO顺序先找到D中的fun()方法,此时C中重写的fun()方法将永远访问不到,导致了C只能继承不能重写D中的方法(即使C重写了fun()方法也不会访问到),这就是深度优先搜索的缺陷;
通过C3 算法解决
DFS、BFS、C3对比
代码理解C3算法
class A(object):
def __init__(self):
print("enter A")
super(A, self).__init__()
print("leave A")
class B(object):
def __init__(self):
print("enter B")
super(B, self).__init__()
print("leave B")
class C(object):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
class D(A):
def __init__(self):
print("enter D")
super(D, self).__init__()
print("leave D")
class E(A, C):
def __init__(self):
print("enter E")
super(E, self).__init__()
print("leave E")
class F(D, B):
def __init__(self):
print("enter F")
super(F, self).__init__()
print("leave F")
class G(E, F):
def __init__(self):
print("enter G")
super(G, self).__init__()
print("leave G")
"""
l[F(D, B)] = F + merge(l(D), l(B), DB)
F + merge([D, A], B, [D, B])
[F, D, A, B]
l[G(E, F)] = G + merge(l(E), l(F), EF)
G + merge([E, A, C], [F, D, A, B], [E, F])
[G, E, F, D, A, C, B]
"""
print(G.mro())
参考来源: