【问题标题】:Why are classes being ordered this way in the MRO?为什么在 MRO 中以这种方式排序课程?
【发布时间】:2017-06-12 03:30:30
【问题描述】:

我对 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

等等

【问题讨论】:

  • 我假设 python3 没有在 python2 上运行(默认不是从对象继承)
  • @user7438048:嗯A可以构造,只是检查mro的方式不同。
  • 您可能对这个演示有用:slides.com/aktech/python-mro#

标签: python python-3.x class multiple-inheritance method-resolution-order


【解决方案1】:

简而言之,因为 C 依赖于 E,正如您在依赖图中看到的那样(O 是 object

Python 的 方法解析顺序 (MRO) 的约束条件是,如果类 a 是类 b 的依赖项,则它是比 b 排在队列后面。

现在更多的是理论:

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

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

L[object] = object

(source)

merge定义为:

取第一个列表的头部,即L[B1][0];如果这个头部不在任何其他列表的尾部,则将其添加到 C 的线性化中并从合并中的列表中删除,否则查看下一个列表的头部并取它,如果它是好头。然后重复这个操作,直到所有的类都被移除或者不可能找到好的磁头。在这种情况下,无法构造合并,Python 2.3 将拒绝创建 C 类并引发异常。

(source)

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

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

让我们首先解决递归调用:

L[D] = D + merge(L[E],L[F]);
L[B] = B + 合并(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)我们因此有已经获得HGF的序列:

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

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

L[E] = E + 合并( (G,O) , (H,O) )

由于O 都在尾部,所以放在最后:

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

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

L[C] = C + 合并( (E,G,H,O) , (G,O) );
L[C] = (C,E) + 合并( (G,H,O) , (G,O) );
L[C] = (C,E,G) + 合并( (H,O) , (O) );
L[C] = (C,E,G,H) + 合并( (O) , (O) );
*L[C] = (C,E,G,H,O)。

还有L[D]

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

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

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

现在我们终于可以解决了:

L[A] = A + 合并( (D,E,G,H,F,O) , (B, C,E,G,H,O) , (E,G,H,O) );
L[A] = (A,D) + 合并( (E,G,H,F,O) , (B,@987654 E,G,H,O) , (E,G,H,O) );
L[@987654502 @] = (A,D,B) + 合并( (E,G,H,F,O) , (C,@987654 G,H,O) , (E,G,H,O) );
L[A] = ( A,D,B,C) + 合并( (E,G,H,F,O) , (G,E, H,O) , (E,G,@987654 536@,O) );
L[A] = (A,D,B,C,E) + 合并( (G,H,F,O) , (G,H,O) , (G,H,O) ;
L[A] = (A,D,B,C,E,G) + 合并( (H,F ,O) , (H,O) , (H,O) );
L[A] = (A,@ 987654570@,B,C,E,G,H) + 合并( (F,O) , (O) , (@98765457) em>;
L[A] = (A,D,B,C,E,G,H,F) +合并( (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']

【讨论】:

  • 如果您添加报价来源会很好。
  • 我认为L[D] = A + merge(L[E],L[F]); 应该是L[D] = D + merge(L[E],L[F]); 对吧?
  • @TakuroWada:是的,这是一个复制粘贴错误。很好发现:)。
  • Willem:你确信你的mro_merge() 函数会产生与 Python 内部相同的结果吗?我之所以问,是因为我正在尝试编写一个元类,它对 MRO 列表中的每个类都执行某些操作,但需要在创建类之前这样做(所以不能调用mro()或使用__mro__,因为它还不存在)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-19
  • 2021-06-21
  • 2013-03-04
  • 1970-01-01
  • 1970-01-01
  • 2018-11-28
相关资源
最近更新 更多