1-Python - 手推C3算法

about

Python3.6.8

了解C3算法有助于我们理解Python中类的继承顺序。
C3算法推到规则:

  • 每一个类的继承顺序都是从基类向子类看
  • 形成一个指向关系的顺序[当前类] + [父类的继承顺序]
  • 进行一次merge
  • 如果一个类出现从左到右的第一个顺序上,并且没有出现在后面的顺序中,或者出现在后面顺序了,但是仍然是第一个,那么就把这个类提取出来

参考下面的示例和应用,你将很快理解上述规则。

示例1

上图:

根据C3算法的规则,推导过程其继承过程:

# class A(object): ...
# class B(A): ...
# class C(A): ...
# class D(B, C): ...
# print(D.__mro__)  # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
'''手推mro,从上至下的顺序
L(A) = [A] + [O]
A = [O]
AO = []
L(A) = AO

L(B) = [B] + [AO]
B = [AO]
BA = [O]
BAO = []
L(B) = BAO

L(C) = [C] + [AO]
C = [AO]
CA = [O]
CAO = []
L(C) = CAO

L(D) = [D] + [BAO] + [CAO]
D = [BAO] + [CAO]
DB = [AO] + [CAO]
DBC = [AO] + [AO]
DBCA = [O] + [O]
DBCAO = []
L(D) = DBCAO'''

示例2

上图:

根据C3算法的规则,推导其继承过程:

# class G(object): ...
# class E(G): ...
# class D(object): ...
# class F(object): ...
# class B(D, E): ...
# class C(D, F): ...
# class A(B, C): ...
# print(A.__mro__)  #  ABCDEGFO  (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.G'>, <class '__main__.F'>, <class 'object'>)
'''手推C3算法
L(G) = [G] + [O]
G = [O]
GO = []
L(G) = GO

L(E) = [E] + [GO]
E = [GO]
EG = [O]
EGO = []
L(E) = EGO

L(D) = [D] + [DO]
D = [O]
DO = []
L(D) = DO

L(F) = [F] + [FO]
F = [O]
FO = []
L(F) = FO

L(B) = [B] + [DO] + [EGO]
B = [DO] + [EGO]
BD = [O] + [EGO]
BDE = [O] + [GO]
BDEG = [O] + [O]
BDEGO = []
L(B) = BDEGO

L(C) = [C] + [DO] + [FO]
C = [DO] + [FO]
CD = [O] + [FO]
CDF = [O] + [O]
CDFO = []
L(C) = CDFO

L(A) = [A] + [BDEGO] + [CDFO]
A = [BDEGO] + [CDFO]
AB = [DEGO] + [CDFO]
ABC = [DEGO] + [DFO]
ABCD = [EGO] + [FO]
ABCDE = [GO] + [FO]
ABCDEG = [O] + [FO]
ABCDEGF = [O] + [O]
ABCDEGFO = []
L(A) = ABCDEGFO
'''

示例3


手推C3算法实现:

"""
L(K) = [KO]

L(G) = [GO]

L(H) = [HKO]

L(M) = [MO]

L(N) = [NO]

L(D) = [D] + [GO] + [HKO]
D = [] + [GO] + [HKO]
DG = [] + [O] + [HKO]
DGH = [] + [O] + [KO]
DGHK = [] + [O] + [O]
DGHKO = [] + [] + []
L(D) = [DGHKO]

L(E) = [EMO]

L(F) = [F] + [MO] + [NO]
F = [] + [MO] + [NO]
FM = [] + [O] + [NO]
FMN = [] + [O] + [O]
FMNO = [] + [] + []
L(F) = [FMNO]

L[B] = [B] + [DGHKO] + [EMO]
B = [] + [DGHKO] + [EMO]
BD = [] + [GHKO] + [EMO]
BDG = [] + [HKO] + [EMO]
BDGH = [] + [KO] + [EMO]
BDGHK = [] + [O] + [EMO]
BDGHKE = [] + [O] + [MO]
BDGHKEM = [] + [O] + [O]
BDGHKEMO = [] + [] + []
L[B] = [BDGHKEMO]

L(C) = [C] + [EMO] + [FMNO]
C = [] + [EMO] + [FMNO]
CE = [] + [MO] + [FMNO]
CEF = [] + [MO] + [MNO]
CEFM = [] + [O] + [NO]
CEFMN = [] + [O] + [O]
CEFMNO = [] + [] + []
L(C) = [CEFMNO]

L(P) = [PO]

L(A) = [A] + [BDGHKEMO] + [CEFMNO] + [PO]
A = [] + [BDGHKEMO] + [CEFMNO] + [PO]
AB = [] + [DGHKEMO] + [CEFMNO] + [PO]
ABD = [] + [GHKEMO] + [CEFMNO] + [PO]
ABDG = [] + [HKEMO] + [CEFMNO] + [PO]
ABDGH = [] + [KEMO] + [CEFMNO] + [PO]
ABDGHK = [] + [EMO] + [CEFMNO] + [PO]
ABDGHKC = [] + [EMO] + [EFMNO] + [PO]
ABDGHKCE = [] + [MO] + [FMNO] + [PO]
ABDGHKCEF = [] + [MO] + [MNO] + [PO]
ABDGHKCEFM = [] + [O] + [NO] + [PO]
ABDGHKCEFMN = [] + [O] + [O] + [PO]
ABDGHKCEFMNP = [] + [O] + [O] + [O]
ABDGHKCEFMNPO = [] + [] + [] + []
L(A) = [ABDGHKCEFMNPO]
"""

应用

看代码,请说出执行流程:

class A(object):

    def __init__(self):
        print("enter A")
        print("leave A")

class B(object):

    def __init__(self):
        print("enter B")
        print("leave B")

class C(A):

    def __init__(self):
        print("enter C")
        super().__init__()
        print("leave C")

class D(A):

    def __init__(self):
        print("enter D")
        super().__init__()
        print("leave D")

class E(B, C):

    def __init__(self):
        print("enter E")
        B.__init__(self)
        C.__init__(self)
        print("leave E")

class F(E, D):

    def __init__(self):
        print("enter F")
        E.__init__(self)
        D.__init__(self)
        print("leave F")

F()
# print(F.__mro__)  # FEBCDA0 (<class '__main__.F'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
# 打印结果如下示例
'''
enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F
'''

解题思路,首先根据代码,画出继承顺序图:

那么这里为什么要用到c3算法,就是为了算出mro顺序(这里只能手推,不能打印)。当算出来mro顺序之后,我们就可以在后面用到了:

'''
L(A) = [A] + [O]
A = [O]
AO = []
L(A) = AO

L(B) = [B] + [O]
B = [O]
BO = []
L(B) = BO

L(C) = [C] + [AO]
C = [AO]
CA = [O]
CAO = []
L(C) = CAO

L(D) = [D] + [AO]
D = [AO]
DA = [O]
DAO = []
L(D) = DAO

L(E) = [E] + [BO] + [CAO]
E = [BO] + [CAO]
EB = [O] + [CAO]
EBC = [O] + [AO]
EBCA = [O] + [O]
EBCAO = []
L(E) = EBCAO

L(F) = [F] + [EBCAO] + [DAO]
F = [EBCAO] + [DAO]
FE = [BCAO] + [DAO]
FEB = [CAO] + [DAO]
FEBC = [AO] + [DAO]
FEBCD = [AO] + [AO]
FEBCDA = [O] + [O]
FEBCDAO = []
L(F) = FEBCDAO
'''

一番推算,mro的顺序为FEBCDAO。接下来,我们开始解释代码的执行流程:

  1. 代码从F()处开始执行,执行其内部的init方法首先打印enter F
  2. 执行E中的init方法,打印一行enter E
  3. 执行B中的init方法,打印enter B,紧接着打印leave B,执行完毕,回到E
  4. 执行C中的init方法,打印enter C,然后调用父类的super方法
    • 那该执行父类的init方法,这里要知道C的父类是谁?是A吗?(注意,关键点来了)
    • 不是!而是顺着mro的顺序查找,C后面是D,所以,执行Dinit方法
    • 首先打印enter D,接着D中又执行super方法,找父类的super方法,mro中是A
    • 所以执行A中的init方法,打印enter A,再打印leave A,然后,程序回到D中,又打印leave D,执行完毕,回到C
    • 打印leave C,执行完毕,程序回到E
    • 打印leave EE此时也执行完毕,程序回到最开始的F
  5. 执行Dinit方法
    • 首先打印enter D
    • 调用父类的super方法,从mro找父类
      • 打印enter A
      • 打印leave A
      • A执行完毕,回到D
    • 打印leave D,执行完毕,回到最开始的F
  6. 打印leave F,程序结束

that's all
posted @ 2018-11-22 20:59  听雨危楼  阅读(611)  评论(1编辑  收藏  举报