为什么 Python 的 C3 MRO 依赖于一个通用的基类?



当我读到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规则的相同简化(子类先于父类,并且尊重子类的顺序(在这里仍然适用。Bobject之前,X也是如此,并且AB仍然以相同的顺序列出。然而,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)

相关内容

  • 没有找到相关文章

最新更新