Python之MRO以及C3算法

203 阅读2分钟

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())

参考来源:

kaiyuan.me/2016/04/27/…

blog.csdn.net/lis_12/arti…