当我读到Python的C3方法解析顺序时,我经常听到它被简化为"孩子先于父母,尊重子类的顺序"。然而,只有当所有子类都继承自同一祖先时,这似乎才成立。
例如
class X():
def FOO(self):
return 11
class A(X):
def doIt(self):
return super().FOO()
def FOO(self):
return 42
class B(X):
def doIt(self):
return super().FOO()
def FOO(self):
return 52
class KID(A,B):
pass
KID的MRO如下:儿童,A,B,X
然而,如果我将B改为:
class B(object):
KID的MRO变为:儿童,A,X,B
在搜索完KID的所有父母之前,我们似乎正在搜索A的超类。
因此,现在它似乎比"孩子优先,广度优先"到"如果共同祖先其他人深度优先,那么孩子优先,宽度优先"有点不直观。
如果一个类停止使用公共祖先,MRO就会发生变化(即使除了那一个链接之外,整个层次结构是相同的(,并且您开始调用更深层次的祖先方法,而不是该类中的方法。
Python 3中的所有类都有一个公共基类object
。您可以从class
定义中省略该类,但除非您已经间接继承了object
,否则它仍然存在。(在Python 2中,您必须显式继承object
才能使用super()
,因为这是一种新型的类特性(。
您将B
的基类从X
更改为object
,但X
也继承自object
。MRO将此考虑在内。C3规则的相同简化(子类先于父类,并且尊重子类的顺序(在这里仍然适用。B
在object
之前,X
也是如此,并且A
和B
仍然以相同的顺序列出。然而,X
应该在B
之前,因为两者都继承自object
,并且子类A(X)
在KID
中位于B
之前。
请注意,没有任何地方说C3是广度优先的。如果有什么不同的话,那就是深度优先。请参阅Python 2.3方法解析顺序,以深入描述该算法及其如何应用于Python,但任何类的线性化都是将基类的线性化与基类本身合并的结果:
L[KID] = KID + merge(L[A], L[B], (A, B))
其中CCD_ 22是该类的C3线性化(它们的MRO(。
因此,在合并时,A
的线性化先于B
,使C3从深度而非广度来看层次结构。合并从最左边的列表开始,取任何没有出现在其他列表尾部的元素(所以除了第一个元素之外的所有元素(,然后取下一个,等等。
在第一个示例中,L[A]
和L[B]
几乎相同(它们都以(X, object)
作为MRO结尾,只有第一个元素不同(,因此合并很简单;合并(A, X, object)
和(B, X, object)
,合并后第一个列表中只有A
,然后是整个第二个列表,在准备KID
:后以(KID, A, B, X, object)
结束
L[KID] = KID + merge((A, X, object), (B, X, object), (A, B))
# ^ ^^^^^^
# & both removed as they appear in the next list
= KID + (A,) + (B, X, object)
= (KID, A, B, X, object)
在第二个示例中,L[A]
是未更改,但L[B]
现在是(B, object)
(删除X
(,因此合并时更喜欢X
而不是B
,因为合并时(A, X, object)
排在第一位,而X
不出现在第二列表中。因此
L[KID] = KID + merge((A, X, object), (B, object), (A, B))
# ^^^^^^
# removed as it appears in the next list
= KID + (A, X) + (B, object)
= (KID, A, X, B, object)