Method Resolution Order (MRO) in new-style classes?
在《简而言之的Python》(第二版)一书中,有一个例子旧样式类来演示如何以经典的解析顺序解析方法,以及和新订单有什么不同?
我用新样式重写了示例,尝试了同样的示例,但是结果与旧样式类得到的结果没有什么不同。我用来运行这个示例的python版本是2.5.2。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Base1(object): def amethod(self): print"Base1" class Base2(Base1): pass class Base3(object): def amethod(self): print"Base3" class Derived(Base2,Base3): pass instance = Derived() instance.amethod() print Derived.__mro__ |
调用
我不确定我对新样式类MRO的理解是否错误,或者我正在犯一个我无法察觉的愚蠢错误。请帮助我更好地理解MRO。
当同一祖先类在"幼稚的"深度优先方法中不止一次出现时,传统类和新型类的解决顺序之间的关键区别就出现了,例如,考虑"菱形继承"情况:
1 2 3 4 5 6 7 8 9 10 | >>> class A: x = 'a' ... >>> class B(A): pass ... >>> class C(A): x = 'c' ... >>> class D(B, C): pass ... >>> D.x 'a' |
这里,传统样式,解析顺序是d-b-a-c-a:所以当查找d.x时,a是解析的第一个基础,因此在c中隐藏定义。同时:
1 2 3 4 5 6 7 8 9 10 11 | >>> class A(object): x = 'a' ... >>> class B(A): pass ... >>> class C(A): x = 'c' ... >>> class D(B, C): pass ... >>> D.x 'c' >>> |
这里,新款,订单是:
1 2 3 | >>> D.__mro__ (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) |
由于
这是应该避免使用旧样式类的原因之一:具有"菱形"模式的多重继承对它们不起作用,而对新样式则起作用。
python的方法解析顺序实际上比仅仅理解菱形模式更复杂。要真正理解它,请看一下c3线性化。我发现在扩展方法跟踪订单时,使用print语句真的很有帮助。例如,您认为这个模式的输出是什么?(注意:"x"假设为两个交叉边,而不是节点,^表示调用super()的方法)
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 30 31 32 33 34 35 36 37 38 39 40 41 42 | class G(): def m(self): print("G") class F(G): def m(self): print("F") super().m() class E(G): def m(self): print("E") super().m() class D(G): def m(self): print("D") super().m() class C(E): def m(self): print("C") super().m() class B(D, E, F): def m(self): print("B") super().m() class A(B, C): def m(self): print("A") super().m() # A^ # / \ # B^ C^ # /| X # D^ E^ F^ # \ | / # G |
你拿到B D C E F G了吗?
1 2 | x = A() x.m() |
经过多次试错,我提出了一个关于C3线性化的非正式图论解释,如下:(如果是错的,请有人告诉我。)
考虑这个例子:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | class I(G): def m(self): print("I") super().m() class H(): def m(self): print("H") class G(H): def m(self): print("G") super().m() class F(H): def m(self): print("F") super().m() class E(H): def m(self): print("E") super().m() class D(F): def m(self): print("D") super().m() class C(E, F, G): def m(self): print("C") super().m() class B(): def m(self): print("B") super().m() class A(B, C, D): def m(self): print("A") super().m() # Algorithm: # 1. Build an inheritance graph such that the children point at the parents (you'll have to imagine the arrows are there) and # keeping the correct left to right order. (I've marked methods that call super with ^) # A^ # / | \ # / | \ # B^ C^ D^ I^ # / | \ / / # / | X / # / |/ \ / # E^ F^ G^ # \ | / # \ | / # H # (In this example, A is a child of B, so imagine an edge going FROM A TO B) # 2. Remove all classes that aren't eventually inherited by A # A^ # / | \ # / | \ # B^ C^ D^ # / | \ / # / | X # / |/ \ # E^ F^ G^ # \ | / # \ | / # H # 3. For each level of the graph from bottom to top # For each node in the level from right to left # Remove all of the edges coming into the node except for the right-most one # Remove all of the edges going out of the node except for the left-most one # Level {H} # # A^ # / | \ # / | \ # B^ C^ D^ # / | \ / # / | X # / |/ \ # E^ F^ G^ # | # | # H # Level {G F E} # # A^ # / | \ # / | \ # B^ C^ D^ # | \ / # | X # | | \ # E^F^ G^ # | # | # H # Level {D C B} # # A^ # /| \ # / | \ # B^ C^ D^ # | | # | | # | | # E^ F^ G^ # | # | # H # Level {A} # # A^ # | # | # B^ C^ D^ # | | # | | # | | # E^ F^ G^ # | # | # H # The resolution order can now be determined by reading from top to bottom, left to right. A B C E D F G H x = A() x.m() |
你得到的结果是正确的。尝试将
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 30 | class Base1(object): def amethod(self): print"Base1" class Base2(Base1): pass class Base3(Base1): def amethod(self): print"Base3" class Derived(Base2,Base3): pass instance = Derived() instance.amethod() class Base1: def amethod(self): print"Base1" class Base2(Base1): pass class Base3(Base1): def amethod(self): print"Base3" class Derived(Base2,Base3): pass instance = Derived() instance.amethod() |
现在它输出:
1 2 | Base3 Base1 |
阅读此解释了解更多信息。
您看到这种行为是因为方法解析是深度优先,而不是宽度优先。德维德的遗产看起来像
1 2 3 | Base2 -> Base1 / Derived - Base3 |
所以
这反映在