为什么 Python 的 C3 MRO 依赖于一个共同的基础 class?

Why does Python's C3 MRO depend on a common base class?

当我读到 Python 的 C3 方法解析顺序时,我经常听到它被缩减为 "children come before parents, and the order of subclasses is respected"。然而,这似乎只有在所有子class继承自同一祖先时才成立。

例如

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的parents.

,我们就在搜索A的superclass

所以现在看起来比 "kids first, breadth first" 到 "kids first, breadth first if common ancestor else depth first" 更不直观。

如果 class 停止使用共同的祖先,MRO 会发生变化(即使整体层次结构与 link 不同,但你开始调用更深层次的祖先方法而不是 class.

中的方法

Python 3 中的所有 class 都有一个共同的基数 class、object。您可以从 class 定义中省略 class,但除非您已经间接继承自 object,否则它就在那里。 (在 Python 2 中,您必须显式继承 object 才能使用 super(),因为这是 new-style class 的功能)。

您将 B 的基数 class 从 X 更改为 object,但是 X 继承自 object。 MRO 更改以考虑到这一点。 C3 规则的相同简化(children 在 parents 之前,子 class 的顺序受到尊重 )在这里仍然适用。 B 出现在 object 之前,X 也是如此,并且 AB 仍然以相同的顺序列出。但是,X 应该出现在 B 之前,因为它们都继承自 object 并且 subclass A(X) 出现在 B 之前 KID ].

请注意,没有任何地方说 C3 是广度优先。如果有的话,那就是深度优先。有关算法的 in-depth 描述及其如何应用于 Python,请参阅 The Python 2.3 Method Resolution Order,但任何 class 的线性化都是合并基数 [=] 的线性化的结果83=]es 加上基数 classes 本身:

L[KID] = KID + merge(L[A], L[B], (A, B))

其中 L[..] 是 class(他们的 MRO)的 C3 线性化。

因此 A 的线性化在合并时出现在 B 之前,使 C3 从深度而不是广度来看待层次结构。合并从 left-most 列表开始,并获取未出现在其他列表尾部的任何元素(因此除了第一个元素之外的所有元素),然后获取下一个,依此类推。

在您的第一个示例中,L[A]L[B] 几乎相同(它们都以 (X, object) 结尾作为它们的 MRO,只有第一个元素不同),所以合并是简单的;你合并 (A, X, object)(B, X, object),合并它们只给你第一个列表中的 A,然后是整个第二个列表,在前缀 [=33= 之后以 (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),所以合并更喜欢 XB 之前,因为 (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)