为什么 类 在 MRO 中以这种方式订购?

Why are classes being ordered this way in the MRO?

我对 Python MRO 有疑问 对于此代码:

class F: pass 
class G: pass 
class H: pass
class E(G,H): pass
class D(E,F): pass 
class C(E,G): pass
class B(C,H): pass
class A(D,B,E): pass

print(A.__mro__)

我得到这个输出:

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

为什么我在 <class '__main__.E'> 之前得到 <class '__main__.C'>

我以为会是:

  1. A
  2. D,B,E
  3. E,F | C,H | G,H

等等

简而言之,因为 C 取决于 E,正如您在 dependency-graph 中看到的那样(O 是 object :

Python 的 方法解析顺序 (MRO) 的工作条件是如果 class a 是 class b 的依赖,它在队列中比 b.

靠后

现在更理论:

在 Python 中,MRO 使用以下 线性化 规则:

L[C(B1 ... Bn)] = C + merge(L[B1] ... L[Bn], B1 ... Bn); and

L[object] = object

(source)

merge定义为:

take the head of the first list, i.e L[B1][0]; if this head is not in the tail of any of the other lists, then add it to the linearization of C and remove it from the lists in the merge, otherwise look at the head of the next list and take it, if it is a good head. Then repeat the operation until all the class are removed or it is impossible to find good heads. In this case, it is impossible to construct the merge, Python 2.3 will refuse to create the class C and will raise an exception.

(source)

所以对于你的情况,第一步是:

L[A] = A + merge(L[D],L[B],L[E])

让我们先解决递归调用:

L[D] = D + merge(L[E],L[F]) ;
L[B] = B + merge(L[C],L[H]);和
L[E] = E + merge(L[G],L[H]).

和更多递归(我们只做H一次,不重做E):

L[F] = F + merge(L[O]);
L[C] = C + merge(L[E],L[G]);
L[G] = G + merge(L[O]);和
L[H] = H + merge(L[O]).

因为 L[O]Omerge(a) (对于一个对象是a) 因此我们已经得到了 H, GF:

的序列

L[H] = (H, O).
L[G] = (G, O).
L[F] = (F, O).

现在我们可以计算出L[E]:

L[E] = E + merge( (G,O) , (H,O) ).

因为O都在尾部,所以放在最后:

L[E] = (E,G,H,O)

现在我们可以计算出L[C]:

L[C] = C + merge( (E,G,H,O) , (G,O) );
L[C] = (C,E) + merge( (G,H,O) , (G,O));
L[C] = (C,E,G) + merge( (H,O) , (O));
L[C] = (C,E,G,H) + merge( (O) , (O));
*L[C] = (C,E,G,H,O).

L[D]:

L[D] = D + merge( (E,G,H,O) , (F,O) );
..;
L[D] = (D,E,G,H,F,O ).

接下来L[B]可以完全解决:

L[B] = B + merge( (C,E,G,H,O) , (H,O) );
..;
L[B] = (B,C,E,G,H,O ).

现在终于可以解决了:

L[A] = A + merge( (D,E,G,H,F,O) , (B,C,E,G,H,O) , (E,G,H,O) );
L[A] = (A,D) + merge( (E,G,H,F,O) , (B,C,E,G,H,O) , (E,G,H,O));
L[A] = (A,D,B) + merge( (E,G,H,F,O) , (C,E,G,H,O) , (E,G,H,O));
L[A] = (A,D,B,C) + merge( (E,G,H,F,O) , (E,G,H,O) , (E,G,H,O));
L[A] = (A,D,B,C,E) + merge( (G,H,F,O) , (G,H,O) , (G ,H,O) );
L[A] = (A,D,B,C,E,G ) + merge( (H,F,O) , (H,O) , (H,O ) );
L[A] = (A,D,B,C,E,G ,H) + merge( (F,O) , (O) , (O) );
L[A] = (A,D,B,C,E,G ,H,F) + merge( (O) , (O) , (O) );
L[A] = (A,D,B,C,E,G ,H,F,O).

这是预期的行为。

我做的一个效率不高的合并函数可以用于教育目的,绝对没有针对生产进行优化:

def mro_merge(*args):
    for i,arg in enumerate(args):
        if len(arg) > 0:
            head = arg[0]
            for argb in args:
                if head in argb[1:]:
                    break
            else:
                newargs = tuple(argb if len(argb) > 0 and argb[0] != head else argb[1:] for argb in args)
                print('mro_merge(%s) = %s + mro_merge(%s)'%(args,head,newargs))
                yield head
                for x in mro_merge(*newargs):
                    yield x
                break

当你调用它时,它会生成:

>>> list(mro_merge(('G','O'),('H','O')))
mro_merge((('G', 'O'), ('H', 'O'))) = G + mro_merge((('O',), ('H', 'O')))
mro_merge((('O',), ('H', 'O'))) = H + mro_merge((('O',), ('O',)))
mro_merge((('O',), ('O',))) = O + mro_merge(((), ()))
['G', 'H', 'O']
>>> list(mro_merge( ('D','E','G','H','F','O') , ('B','C','E','G','H','O') , ('E','G','H','O') ))
mro_merge((('D', 'E', 'G', 'H', 'F', 'O'), ('B', 'C', 'E', 'G', 'H', 'O'), ('E', 'G', 'H', 'O'))) = D + mro_merge((('E', 'G', 'H', 'F', 'O'), ('B', 'C', 'E', 'G', 'H', 'O'), ('E', 'G', 'H', 'O')))
mro_merge((('E', 'G', 'H', 'F', 'O'), ('B', 'C', 'E', 'G', 'H', 'O'), ('E', 'G', 'H', 'O'))) = B + mro_merge((('E', 'G', 'H', 'F', 'O'), ('C', 'E', 'G', 'H', 'O'), ('E', 'G', 'H', 'O')))
mro_merge((('E', 'G', 'H', 'F', 'O'), ('C', 'E', 'G', 'H', 'O'), ('E', 'G', 'H', 'O'))) = C + mro_merge((('E', 'G', 'H', 'F', 'O'), ('E', 'G', 'H', 'O'), ('E', 'G', 'H', 'O')))
mro_merge((('E', 'G', 'H', 'F', 'O'), ('E', 'G', 'H', 'O'), ('E', 'G', 'H', 'O'))) = E + mro_merge((('G', 'H', 'F', 'O'), ('G', 'H', 'O'), ('G', 'H', 'O')))
mro_merge((('G', 'H', 'F', 'O'), ('G', 'H', 'O'), ('G', 'H', 'O'))) = G + mro_merge((('H', 'F', 'O'), ('H', 'O'), ('H', 'O')))
mro_merge((('H', 'F', 'O'), ('H', 'O'), ('H', 'O'))) = H + mro_merge((('F', 'O'), ('O',), ('O',)))
mro_merge((('F', 'O'), ('O',), ('O',))) = F + mro_merge((('O',), ('O',), ('O',)))
mro_merge((('O',), ('O',), ('O',))) = O + mro_merge(((), (), ()))
['D', 'B', 'C', 'E', 'G', 'H', 'F', 'O']