about
Python3.6.8
了解C3算法有助于我们理解Python中类的继承顺序。 C3算法推到规则:
- 每一个类的继承顺序都是从基类向子类看
- 形成一个指向关系的顺序[当前类] + [父类的继承顺序]
- 进行一次merge
- 如果一个类出现从左到右的第一个顺序上,并且没有出现在后面的顺序中,或者出现在后面顺序了,但是仍然是第一个,那么就把这个类提取出来
参考下面的示例和应用,你将很快理解上述规则。
示例1
上图:
根据C3算法的规则,推导过程其继承过程:
python
# 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算法的规则,推导其继承过程:
python
# 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算法实现:
python
"""
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]
"""
应用
看代码,请说出执行流程:
python
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顺序之后,我们就可以在后面用到了:
python
'''
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。接下来,我们开始解释代码的执行流程:
- 代码从
F()
处开始执行,执行其内部的init
方法首先打印enter F
- 执行
E
中的init
方法,打印一行enter E
- 执行
B
中的init
方法,打印enter B
,紧接着打印leave B
,执行完毕,回到E
中 - 执行
C
中的init
方法,打印enter C
,然后调用父类的super
方法- 那该执行父类的
init
方法,这里要知道C
的父类是谁?是A
吗?(注意,关键点来了) - 不是!而是顺着mro的顺序查找,
C
后面是D
,所以,执行D
的init
方法 - 首先打印
enter D
,接着D
中又执行super
方法,找父类的super
方法,mro
中是A
- 所以执行
A
中的init
方法,打印enter A
,再打印leave A
,然后,程序回到D
中,又打印leave D
,执行完毕,回到C
中 - 打印
leave C
,执行完毕,程序回到E
中 - 打印
leave E
,E
此时也执行完毕,程序回到最开始的F
中
- 那该执行父类的
- 执行
D
中init
方法- 首先打印
enter D
- 调用父类的
super
方法,从mro
找父类- 打印
enter A
- 打印
leave A
A
执行完毕,回到D
中
- 打印
- 打印
leave D
,执行完毕,回到最开始的F
中
- 首先打印
- 打印
leave F
,程序结束。
that's all