为什么 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
也是如此,并且 A
和 B
仍然以相同的顺序列出。但是,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
),所以合并更喜欢 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)
当我读到 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
也是如此,并且 A
和 B
仍然以相同的顺序列出。但是,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
),所以合并更喜欢 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)