在计算机科学中,C3算法主要用于确定多重继承时,子类应该继承哪一个父类的方法,即方法解析顺序(Method Resolution Order,MRO)。
C3算法实现了三种重要特性:
1996年的OOPSLA会议上,论文"A Monotonic Superclass Linearization for Dylan(英语:Dylan (programming language))"首次提出了C3超类线性化。被Python 2.3选作方法解析的默认算法。Perl 6 Parrot,, Solidity, PGF/TikZ(英语:PGF/TikZ)等面向对象语言。也作为可选的、默认不是MRO的语言如Perl 5从版本5.10.0. 早期版本Perl 5的一个扩展实现,称为Class::C3
发布于CPAN.
一个类的C3超类线性化是这个类,再加上它的各个父类的线性化与各个父类形成列表的唯一合并的结果。父类列表作为合并过程的最后实参,保持了直接父类的本地前趋序。
各个父类的线性化与各个父类形成列表的合并算法,首先选择不出现在各个列表的尾部(指除了第一个元素外的其他元素)的第一个元素,该元素可能同时出现在多个列表的头部。被选中元素从各个列表中删除并追加到输出列表中。这个选择再删除、追加元素的过程迭代下去直到各个列表被耗尽。如果在某个时候无法选出元素,说明这个类继承体系的依赖序是不一致的,因而无法线性化。
给定
class Oclass A extends Oclass B extends Oclass C extends Oclass D extends Oclass E extends Oclass K1 extends A, B, Cclass K2 extends D, B, Eclass K3 extends D, Aclass Z extends K1, K2, K3
Z的线性化计算:
L(O) := // the linearization of O is trivially the singleton list , because O has no parentsL(A) := + merge(L(O), ) // the linearization of A is A plus the merge of its parents' linearizations with the list of parents... = + merge(, ) = // ...which simply prepends A to its single parent's linearizationL(B) := // linearizations of B, C, D and E are computed similar to that of AL(C) := L(D) := L(E) := L(K1) := + merge(L(A), L(B), L(C), ) // first, find the linearizations of K1's parents, L(A), L(B), and L(C), and merge them with the parent list = + merge(, , , ) // class A is a good candidate for the first merge step, because it only appears as the head of the first and last lists = + merge(, , , ) // class O is not a good candidate for the next merge step, because it also appears in the tails of list 2 and 3; but class B is a good candidate = + merge(, , , ) // class C is a good candidate; class O still appears in the tail of list 3 = + merge(, , ) // finally, class O is a valid candidate, which also exhausts all remaining lists = L(K2) := + merge(L(D), L(B), L(E), ) = + merge(, , , ) // select D = + merge(, , , ) // fail O, select B = + merge(, , , ) // fail O, select E = + merge(, , ) // select O = L(K3) := + merge(L(D), L(A), ) = + merge(, , ) // select D = + merge(, , ) // fail O, select A = + merge(, ) // select O = L(Z) := + merge(L(K1), L(K2), L(K3), ) = + merge(, , , ) // select K1 = + merge(, , , ) // fail A, select K2 = + merge(, , , ) // fail A, fail D, select K3 = + merge(, , ) // fail A, select D = + merge(, , ) // select A = + merge(, , ) // select B = + merge(, , ) // select C = + merge(, , ) // fail O, select E = + merge(, , ) // select O = // done
例子的Python实现
以下为C3算法Python实现的一个例子
def c3MRO(cls): if cls is object: #讨论假设顶层基类为object,递归终止 return #构造C3-MRO算法的总式,递归开始 mergeList = mergeList.append(list(cls.__bases__)) mro = + merge(mergeList) return mrodef merge(inLists): if not inLists: #若合并的内容为空,返回空list #配合下文的排除空list操作,递归终止 return #遍历要合并的mro for mroList in inLists: #取头 head = mroList #遍历要合并的mro(与外一层相同),检查尾中是否有头 ###此处也遍历了被取头的mro,严格地来说不符合标准算法实现 ###但按照多继承中地基础规则(一个类只能被继承一次), ###头不可能在自己地尾中,无影响,若标准实现,反而增加开销 for cmpList in inLists: if head in cmpList: break else: #筛选出好头 nextList = for mergeItem in inLists: if head in mergeItem: mergeItem.remove(head) if mergeItem: #排除空list nextList.append(mergeItem) #递归开始 return + merge(nextList) else: #无好头,引发类型错误 raise TypeError