我们都已经知道,Python 3(Python 2 的新式类)中多继承模式是使用 C3 算法来确定 MRO(Method Resolution Order) 的。
下面就讲解C3算法具体是怎么计算的。
MRO计算规则首先来定义一些符号:
用CN表示一个类:C1, C2, C3, ..., CN
C1 C2 C3 ... CN 表示的是一个包含多个类的列表
其中:
head = C1
tail = C2 ... CN
加法运算:
C + (C1 C2 ... CN) = C C1 C2 ... CN
L[C]表示类C的线性化,其实就是C的MRO,比如有个类:
class C(B1, B2, ..., BN): pass
那么:
L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)
如果C是没有父级的object类,则线性化是简单的:
L[object] = object
merge的计算规则如下:
take the head of the first list, i.e L[B1][0];
if this head is not in the tail of any of the other lists, then add it to the linearization of C and remove it from the lists in the merge, otherwise look at the head of the next list and take it, if it is a good head.
Then repeat the operation until all the class are removed or it is impossible to find good heads. In this case, it is impossible to construct the merge, Python 2.3 will refuse to create the class C and will raise an exception.
如果类C仅有一个父类,在这种情况合并的计算是简单的
L[C(B)] = C + merge(L[B], B) = C + L[B]
计算MRO
先从一个简单的例子开始说:
class B(object): pass
L[B] = L[B(object)]
= B + merge(L[object], object)
= B + L[object]
= B object
>>> B.mro()
[, ]
简单子类:
class C(B): pass
L[C] = L[C(B)]
= C + merge(L[B], B)
= C + L[B]
= C B object # 从上面已经知道了L[B] = B object
>>> C.mro()
[, , ]
下面看一个复杂的例子:
O = object
class F(O): pass
class E(O): pass
class D(O): pass
class C(D, F): pass
class B(D, E): pass
class A(B, C): pass
很容易计算的是:
L[O] = O = object
L[F] = L[F(O)] = F O
L[E] = L[E(O)] = E O
L[D] = L[D(O)] = D O
然后来计算相对复杂的C,B,A:
L[C]:
L[C] = L[C(D, F)]
= C + merge(L[D], L[F], DF)
# 从前面可知 L[D] 和 L[F] 的结果
= C + merge(DO, FO, DF)
# 因为 D 是顺序在第一个并且在几个包含 D 的 list 中是 head
# 所以这一次取 D 同时从列表中删除 D
= C + D + merge(O, FO, F)
# 因为 O 虽然是顺序第一个但在其他 list(FO) 中不是head,则跳过,
# 改为检查第二个list FO
# F 是第二个 list 和其他 list 的 head
# 取 F 同时从列表中删除 F
= C + D + F + merge(O)
= C D F O
>>> C.mro()
[, , , ]
L[B]
L[B] = L[B(D, E)]
= B + merge(L[D], L[E], DE)
= B + merge(DO, EO, DE)
= B + D + merge(O, EO, E)
= B + D + E + merge(O)
= B D E O
>>> B.mro()
[, , , ]
L[A]
L[A] = L[A(B, C)]
= A + merge(L(B), L(C), BC)
= A + merge(BDEO, CDFO, BC)
= A + B + merge(DEO, CDFO, C)
# 因为第一个 list 的 head D 不是其他 list(CDFO) 的 head
# 所以改为从下一个 list CDFO 开始
= A + B + C + merge(DEO, DFO)
= A + B + C + D + merge(EO, FO)
= A + B + C + D + E + merge(O, FO)
= A + B + C + D + E + F + merge(O)
= A B C D E F O
>>> A.mro()
[, , , , , , ]
并非所有类都接受线性化,在复杂的层次结构中,有些情况下无法计算出一个类的mro,比如下面这个例子:
O = object
class X(O): pass
class Y(O): pass
class A(X,Y): pass
class B(Y,X): pass
假如要从A和B派生新的类C,我们来尝试计算L[C]
L[C] = L[C(A, B)]
= C + merge(L[A], L[B], AB)
= C + merge(AXYO, BYXO, AB)
= A + B + merge(XYO, YXO)
此时,我们无法合并列表XYO和YXO,因为X在YXO的尾部,而Y在XYO的尾部,因此没有一个符合要求的类,所以C3算法停止。
在python2.3之后的版本中创建这个类时会引发错误,并拒绝创建这个类。
参考资料
The Python 2.3 Method Resolution Order | Python.org
作者:mofei12138