• Python的程序结构[2] -> 类/Class[2] -> 方法解析顺序 MRO


    方法解析顺序 / MRO (Method Resolution Order)


    关于方法解析顺序(MRO)的详细内容可以参考文末链接,这里主要对 MRO 进行简要的总结说明以及一些练习示例。

    经典类和新式类的 MRO


    经典类

    描述: 一种不能继承的类,如果经典类为父类,其子类调用父类的构造函数时会报错。且不具备 __mro__ 属性。

    MRO: Deepth First Search (DFS) / 深度优先搜索

    新式类

    描述: 新式类是一种为了解决经典类中只能继承不能重写的问题而引入的,新式类默认继承自 object,且子类可以调用父类构造函数,在 Python 之后均为新式类。

    MRO: C3算法

    下面的代码以 Python2.7 和 Python3.4 分别运行将产生不一样的结果,这是由于在 Python3 中默认使用了新式类,因此采用了 C3 搜索,而 Python2 中默认为经典类,采用 DFS 搜索,因此产生了不同,若将 A 的基类指定为 object 则输出将变为相同。

     1 """
     2     A
     3    /
     4   /  
     5  B    C
     6     /
     7    /
     8    D
     9 """
    10 from distutils.log import warn as printf
    11 
    12 class A:
    13     def __init__(self):
    14         pass
    15 
    16     def show(self):
    17         printf("This is A")
    18 
    19 
    20 class B(A): pass
    21 
    22 
    23 class C(A):
    24     def show(self):
    25         printf("This is C")
    26 
    27 class D(B, C): pass
    28 
    29 d = D()
    30 d.show()    
    31 # Python2 --> This is A
    32 # Python3 --> This is C

    DFS / BFS / C3


    主要以正常继承和菱形继承为例分别说明三种算法的缺陷和优势。

    DFS深度优先搜索

    利用深度优先搜索,对于正常继承可以很好的完成搜索,但当出现菱形继承时,则会出现搜索的缺陷,在菱形继承中 MRO 的顺序为 ABDC,若此时 C 重写了 D 的方法,可由于继承顺序的问题,将导致优先搜索到 D 的方法,也就是说,C 的重写无效的,C 对于 D 只能继承,不能重写。

     

    BFS广度优先搜索

           广度优先搜索则恰好和深度优先搜索相反,在菱形继承中能够很好的完成搜索,而在普通搜索中却出现了一定缺陷,普通搜索的顺序为ABCDE,但这却违反了单调性原则,即B和C是两个互不相关的父类,在B搜索结束后应该优先搜索D而非C。

    C3算法

           基于上述的两种算法缺陷,Python2.3之后的新式类中MRO全都采用了C3算法,这种算法解决了DFS的只能继承无法重写的问题和BFS的单调性问题。

    Merge List公式


    C3 算法的继承模式主要采用了 merge list 公式,首先假设类的线性化 MRO 记为 L[C]=[C1, C2, C3],则称 C1 为 L[C] 的头,其余为尾。若 C 继承自 B1,B2…,则可以根据以下公式计算出 L[C],

    L[object] = [object]   
    L[C(B1…BN)] = [C] + merge(L[B1]…L[BN], [B1]…[BN])   

    整个计算公式的关键在于 merge,输入为一组列表,输出为一个列表,输出方式为,

    1. 检查第一个列表的头元素(如 L[B1] 的头),记为 H;

    2. 若 H 未出现在其他列表的尾部,则输出该元素,并删除所有列表中的 H,然后重复步骤 1。若 H 出现在其他列表的尾部,则取下一个列表的头部继续步骤。

    3. 重复步骤直至列表为空,则算法结束,若列表不为空且无法输出元素,则说明无法构建继承关系,此时抛出异常。

    下面分别举例来构建一个异常的继承关系和正常的继承进行对比,

    异常继承

     1 """
     2      ________
     3     /           
     4     X   Y    |
     5       /   /
     6      /   /
     7       A   B
     8         /
     9        /
    10        C
    11 """
    12 
    13 class X(object): pass
    14 
    15 class Y(object): pass
    16 
    17 class A(X, Y): pass
    18 
    19 class B(Y, X): pass
    20 
    21 class C(A, B): pass
    22 
    23 print(C.__mro__)

    这段代码最终会报错,

    Traceback (most recent call last):
      File "C:UsersEKELIKEDocumentsPython Note3_Program_Structure3.3_Classmro_c3_error.py", line 21, in <module>
        class C(A, B): pass
    TypeError: Cannot create a consistent method resolution
    order (MRO) for bases X, Y

    原因在于 merge 公式计算到最后无法输出参数,即构建继承失败,计算过程如下,

    C3 Algothrim Calculation:
    L[object] = [object]
    L[X] = [X] + merge(L[object]) = [X, object]
    L[Y] = [Y] + merge(L[object]) = [Y, object]
    L[A] = [A] + merge(L[X], L[Y], [X], [Y]) = [A, X, Y, object]
    L[B] = [B] + merge(L[Y], L[X], [Y], [X]) = [B, Y, X, object]
    L[C] = [C] + merge(L[A], L[B], [A], [B])
         = [C] + merge([A, X, Y, object], [B, Y, X, object], [A], [B])
         = [C, A, B] + merge([X, Y, object], [Y, X, objcet])
    --> Raise Error

    正常继承

    下面给出几个正常继承的示例及其代码,

     1 # C3 MRO
     2 # ----------------------
     3 """
     4     Object
     5     /  |  
     6    /   |   
     7   X _  Y   Z
     8     /  /
     9     / /
    10     A  B
    11      /
    12      C
    13 """
    14 
    15 class X(object): pass
    16 
    17 class Y(object): pass
    18 
    19 class Z(object): pass
    20 
    21 class A(X, Y): pass
    22 
    23 class B(X, Z): pass
    24 
    25 class C(A, B): pass
    26 
    27 print(C.__mro__)
    28 # ----------------------
     1 # ----------------------
     2 """
     3     Object
     4     /  |  
     5    /   |   
     6   D    E    F
     7      /   /
     8     /   /
     9     J    K
    10        /
    11       L
    12 """
    13 
    14 class D(object): pass
    15 
    16 class E(object): pass
    17 
    18 class F(object): pass
    19 
    20 class J(D, E): pass
    21 
    22 class K(E, F): pass
    23 
    24 class L(J, K): pass
    25 
    26 print(L.__mro__)

    其中第二种继承的MRO计算公式如下,

    C3 Algorithm Calculation:
    L[object] = [object]
    L[D] = [D] + merge(L[object]) = [D, object]
    L[E] = [E] + merge(L[object]) = [E, object]
    L[F] = [F] + merge(L[object]) = [F, object]
    L[J] = [J] + merge(L[D], L[E], [D], [E]) = [J, D, E, object]
    L[K] = [K] + merge(L[E], L[F], [E], [F]) = [K, E, F, object]
    L[L] = [L] + merge(L[J], L[K], [J], [K]) 
         = [L, J] + merge([D, E, object], [K, E, F, object], [K])
         = [L, J, D] + merge([E, object], [K, E, F, object], [K])
         = [L, J, D, K] + merge([E, object], [E, F, object])
         = [L, J, D, K, E] + merge([object], [F, object])
         = [L, J, D, K, E, F] + merge([object], [object])
         = [L, J, D, K, E, F, object]

    查看MRO


    当然,在使用时我们不必要每次都对类继承顺序进行计算,在 Python 中可以使用以下两种方式来查看一个类的 MRO。

     1 import inspect
     2 
     3 # Diamond inherit
     4 class A: pass
     5 
     6 class B(A): pass
     7 
     8 class C(A): pass
     9 
    10 class D(B, C): pass
    11 
    12 print(inspect.getmro(D))
    13 print(D.__mro__)

    最终得到输出的结果如下,这一结果即是通过 MRO C3 算法进行计算得出的。

    (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

    参考链接


    http://ju.outofmemory.cn/entry/137896

    http://blog.csdn.net/lis_12/article/details/52859376

  • 相关阅读:
    声明了变量并赋了初始值,但在VS中报当前上下文中不存在名称“ XXX”的错误 Kevin
    delphi中的nil值 Kevin
    MVC3 + Ajax 图片上传总结 Kevin
    EntityFramework 更新数据库的问题 Kevin
    MVC3 EntityFramework 插入Mysql数据库 乱码问题 Kevin
    MVC Html 编码问题 Kevin
    周鸿祎:共享软件无合理商业模式才变流氓
    C++界面库 Xtreme Toolkit Pro[转载]
    《创业10个小诀窍》
    软件商业模式分析之-迅雷
  • 原文地址:https://www.cnblogs.com/stacklike/p/8098321.html
Copyright © 2020-2023  润新知