关于python:这个类层次结构的mro对于旧样式和新样式是相同的吗?

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

我正在阅读python 2.3mro文章,其中给出了以下类层次结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> 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.

1
2
3
4
>>> 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是深度优先的,从左到右,那么前面的类层次结构的旧样式mro应该是不同的,即abeodocdofo。因此,"懒惰的程序员"不能直接从python 2.2获得其MRO。对吗?

例如,在以下代码段上(不要在python 3.x上运行它):

1
2
3
4
5
6
7
8
9
10
11
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等)。

或在这里:

1
2
3
4
5
6
7
8
9
10
11
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的新手,所以我想我错过了什么?


请参阅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写的不是来自你问题中链接的2.3 mro页面,而是来自旧的2.2 mro页面,在其中你会发现提到:

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.


问题是,无论是在Python2.2、2.3或更高版本中,这些都是新样式的类。这里有3个方法解决方案,而不是2个:

  • 经典类方法分辨率。
  • python 2.2新型类方法解析。
  • python 2.3+c3线性化。
  • 方案2和方案3将为给定示例生成相同的MRO。

    如果您想查看(现在几乎完全不相关)python 2.2方法解析方案,那么最完整的文档可能是guido的旧python history博客中的一篇博客文章。还有一个针对python 2.2的旧的归档文档草稿,它主要描述了它,但没有提到一个特殊的案例。


    旧样式类没有内置的mro方法;只有新样式类有。

    另外,请注意,实际的示例中有O = object,而不是class O: pass,从而生成了这些新样式的类。

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

    所以这段话只是说,一个懒惰的程序员如果不想通过这个练习来工作,而且还没有2.3解释器的访问权,可以把这个代码粘贴到2.2解释器中,然后调用A.mro(),得到答案,因为这个例子恰好是2.2和2.3新型类规则给出相同的结果。回答。

    相比之下,一个不懒惰的程序员会想知道他们什么时候做,什么时候不给出相同的答案,而不仅仅是依赖于他们有时做的事实。