2

当我读到 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是:KID, A, B, X

但是,如果我将 B 改为:

class B(object):

KID 的 MRO 变为:KID、A、X、B

似乎我们在搜索完所有 KID 的父母之前正在搜索 A 的超类。

因此,现在它似乎比“儿童优先,广度优先”到“儿童优先,如果有共同祖先,则广度优先”更不直观,否则深度优先。

如果一个类停止使用一个共同的祖先,那么 MRO 会发生变化(即使除了那个链接之外整个层次结构是相同的),并且您开始调用更深层次的祖先方法而不是该类中的方法,这将是一个很大的问题。

4

1 回答 1

2

Python 3 中的所有类都有一个共同的基类,object. 您可以从定义中省略该类class,但除非您已经间接继承自object. (在 Python 2 中,您必须显式继承 fromobject才能使用,super()因为这是一种新型类功能)。

B您将from的基类更改Xobject,但X 继承自object。MRO 进行了更改以考虑到这一点。C3 规则的相同简化(孩子在父母之前,并且尊重子类的顺序)在这里仍然适用。B在 之前objectX和 ,AB仍然以相同的顺序列出。但是,X应该在之前B,因为继承自object和子类A(X)都在B之前KID

请注意,没有任何地方说 C3 是广度优先的。如果有的话,那就是深度优先。有关算法的深入描述以及它如何应用于 Python,请参阅Python 2.3 方法解析顺序,但是任何类的线性化都是合并基类的线性化加上基类本身的结果:

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

L[..]该类别的C3线性化(他们的MRO)在哪里。

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

在您的第一个示例中,L[A]并且L[B]几乎相同(它们都以(X, object)MRO 结尾,只有第一个元素不同),因此合并很简单;你合并(A, X, object)and (B, X, object),合并这些只给你A第一个列表,然后是整个第二个列表,最后是(KID, A, B, X, object)after prepending KID

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]is changed,但L[B]现在是(B, object)(dropping 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)
于 2016-09-14T10:43:41.670 回答