C3线性化
MRO简介
对于支持继承的编程语言来说,某个类的方法或者属性可能定义在当前类,也可能定义在base类中,因此在方法调用的时候需要根据当前类和继承关系确定调用方法的位置,搜索类方法(属性)的顺序即为 方法解析顺序(Method Resolution Order:MRO)。对于只支持单继承的语言来说,MRO很简单,但是对于支持多继承的语言(如Python)来说,MRO比较复杂。
如果把类的继承关系理解成图,则MRO可以理解为从当前节点开始进行搜索,方法有深搜和广搜两类,但是深搜解决不了菱形继承问题,广搜解决不了如下所示的问题,因此Python中使用的为C3算法。
C3线性化
C3线性化的三个特性:
- 基于一致性扩展的优先图。
- 局部优先原则(比如A继承B和C,C继承B,那么A读取父类方法,应该优先读取C的方法而不是B的方法)。
- 单调性原则:子类要和父类方法的搜索顺序一致。例如:如下图所示的继承关系,采用Python2.2 新式类的MRO(从左至右的深度优先遍历,遍历中出现的重复类只保留最后一个),对于A来说,其搜索顺序为[A, X, Y, O],对于B来说,其搜索顺序为[B, Y, X, O],对于C来说,其搜索顺序为[C,A,B,X,Y,O],B和C搜索顺序中X和Y是相反的,这就会导致B优先继承Y中的方法,而继承自B的C反而优先继承X的方法,因此会导致一些不易察觉的错误。
C3算法的实施过程:
记N个类的列表为:
C
1
C
2
.
.
.
C
N
C_1C_2...C_N
C1C2...CN,记:
h
e
a
d
(
C
1
C
2
.
.
.
C
N
)
=
C
1
head(C_1C_2...C_N)=C_1
head(C1C2...CN)=C1
t
a
i
l
(
C
1
C
2
.
.
.
C
N
)
=
C
2
.
.
.
C
N
tail(C_1C_2...C_N)=C_2...C_N
tail(C1C2...CN)=C2...CN
记列表连接操作为:
C
1
+
(
C
2
.
.
.
C
N
)
=
C
1
C
2
.
.
.
C
N
C_1+(C_2...C_N)=C_1C_2...C_N
C1+(C2...CN)=C1C2...CN
假设类C继承自父类
B
1
,
.
.
.
,
B
N
B_1,...,B_N
B1,...,BN,根据C3线性化算法,类C的MRO可以通过如下公式确定:
L
[
C
(
B
1
,
.
.
.
,
B
N
)
]
=
C
+
m
e
r
g
e
(
L
(
B
1
)
,
.
.
.
,
L
(
B
N
)
,
B
1
,
.
.
.
,
B
N
)
L[C(B_1,...,B_N)]=C+merge(L(B1),...,L(B_N),B_1,...,B_N)
L[C(B1,...,BN)]=C+merge(L(B1),...,L(BN),B1,...,BN)
上述公式表明C的解析列表是通过其父类的解析列表及其父类自己merge操作得到。
merge操作可以分为以下几个步骤:
- 选取merge的第一个列表为当前列表K。
- 令 h = h e a d ( K ) h=head(K) h=head(K),如果h没有出现在其他任何列表的tail中,那么把其加入到类C的线性化列表中,并将其从所有列表中移除,之后重复步骤2;
- 否则,设置K为merge中的下一个列表,并重复步骤2的操作;
- 如果merge中的类都被移除,则输出类创建成功;如果不能找到下一个h,则拒绝创建类C并抛出异常。
参考链接:
https://blog.csdn.net/weixin_39910711/article/details/108347484
https://zh.wikipedia.org/wiki/C3%E7%BA%BF%E6%80%A7%E5%8C%96