这个 class 层次结构的 MRO 对于旧式和新式是否相同?

Does the MRO of this class hierarchy is the same for old-style and new-style?

我正在阅读 Python 2.3 MRO 文章,其中给出了以下 class 层次结构:

>>> O = object
>>> class F(O): pass
>>> class E(O): pass
>>> class D(O): pass
>>> class C(D,F): pass
>>> class B(E,D): pass
>>> class A(B,C): pass

                           6
                          ---
Level 3                  | O |
                       /  ---  \
                      /    |    \
                     /     |     \
                    /      |      \
                  ---     ---    ---
Level 2        2 | E | 4 | D |  | F | 5
                  ---     ---    ---
                   \      / \     /
                    \    /   \   /
                     \  /     \ /
                      ---     ---
Level 1            1 | B |   | C | 3
                      ---     ---
                       \       /
                        \     /
                          ---
Level 0                0 | A |
                          ---

在接下来的段落中,下面的引述让我感到困惑:

A lazy programmer can obtain the MRO directly from Python 2.2, since in this case it coincides with the Python 2.3 linearization.

>>> A.mro()
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>,
<class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>,
<type 'object'>)

我感到困惑的原因是文章前面提到:

Classic classes maintain their old method resolution order, depth first and then left to right.

问题:如果旧式MRO是深度优先,从左到右,那么上述class层级的旧式MRO应该不同,即 ABEODOCDOFO。因此,"lazy programmer" 无法直接从 Python 2.2 获取其 MRO。是吗?

例如,在以下片段中(不要 运行 它在 Python 3.x 中):

class O: 
    x = 'o'
class F(O): pass
class E(O): pass
class D(O): pass
class C(D,F): 
    x = 'c'
class B(E,D): pass
class A(B,C): pass

print A.x

如果旧式 MRO 与新型 MRO(即 ABEC 等)相同,我希望它打印 c。但是,它会打印 o(即它是 ABEO 等)。

或在这里:

class O: pass
class F(O): pass
class E(O): pass
class D(O):
    x = 'd'
class C(D,F): 
    x = 'c'
class B(E,D): pass
class A(B,C): pass

print A.x

如果旧式 MRO 与新型 MRO(即 ABEC 等)相同,我希望它打印 c。但是,它会打印 d(即它是 ABEOD 等)。

我是 Python 的新手,所以我想我错过了什么?

问题是,无论是在Python 2.2、2.3 还是以后,这些都是new-style class。这里有3个方法解析方案,不是2:

  1. 经典class方法解析。
  2. Python 2.2 new-style class 方法解析.
  3. Python 2.3+ C3 线性化。

方案 2 和方案 3 将为给定示例生成相同的 MRO。

如果您想查看(现在几乎完全不相关的)Python 2.2 方法解析方案,最完整的文档可能是 a blog post from Guido's old Python history blog. There's also an old archived draft 的 Python 2.2 文档,其中大部分对其进行了描述,但未提及特殊情况。

Old-style classes 没有 mro 方法 built-in;只有 new-style class 会。

此外,请注意实际示例中有 O = object,而不是 class O: pass,因此这些 new-style class 是

这个例子是 reader 练习的一部分。

所以这一段只是说不想完成示例练习并且还没有访问 2.3 解释器的懒惰程序员可以将这段代码粘贴到 2.2 解释器中,然后调用 A.mro() 并得到答案——因为这种情况恰好是 2.2 和 2.3 new-style class 规则给出相同答案的情况。

相比之下,non-lazy 程序员会希望在他们给出相同答案和不给出相同答案时学习,而不仅仅是指望他们有时会给出相同的答案。

参见 the blog post from Guido(2010 年 6 月),特别是以下重要细节:

The computation of the MRO was officially documented as using a depth-first left-to-right traversal of the classes as before. If any class was duplicated in this search, all but the last occurrence would be deleted from the MRO list.

所以你有 ABEODOCDOFO,是的,但是之前的那些 O 被删除了!

Guido 写的 "officially documented" 声明不是来自您问题中链接的 2.3 MRO 页面,而是来自较旧的 2.2 MRO page,您会在其中找到提到的内容:

Using the classic lookup rule, construct the list of classes that would be searched, including duplicates. Now for each class that occurs in the list multiple times, remove all occurrences except for the last.