在 Python 2 中,如果使用的是旧式类(不继承自 object),仍然存在这样的算法。下

面是 Python 2 中旧式类的旧式方法解析的示例:

      class CommonBase:

          def method(self):

              print('CommonBase')

      class Base1(CommonBase):

          pass

         

68 第3章 语法最佳实践—类级别以上

       class Base2(CommonBase):

          def method(self):

              print('Base2')

      class MyClass(Base1, Base2):

          pass

在交互式会话中运行以下代码,可以看到,Base2.method()没有被调用,虽然在类 层次结构中 Base2 比 CommonBase 要更近一些:

   >>> MyClass().method()

   CommonBase

这样的继承情景是极其少见的,因此这更多的是一个理论问题而不是实践问题。标准库不 用这种方式构造继承的层次结构,许多开发人员也都认为这是不好的实践。但由于在类型层次 结构顶部引入了object,在语言的C边(C side)出现了多重继承问题,进而导致了子类化 时的冲突。还要注意,现在 Python 3 中所有类都具有相同的共同祖先。由于使用现有 MRO 使 其正常工作要花费太多的精力,所以提供一个新的 MRO 是更为简单、快捷的解决方案。

因此,在 Python 3 中运行同样的示例,会给出以下不同的结果:

      class CommonBase:

          def method(self):

              print('CommonBase')

      class Base1(CommonBase):

          pass

      class Base2(CommonBase):

          def method(self):

              print('Base2')

      class MyClass(Base1, Base2):

          pass

这种用法表明,C3 序列化会挑选最接近的祖先的方法:

   >>> MyClass().method()

   Base2

注意,在 Python 2 中,如果 CommonBase 类没有显式 继承自 object,那么是无法重复上述结果的。在 Python 3 中,虽然指定 object 为类的祖先是冗余的, 但这么做非常有用,其原因已在上一节中说明。

Python MRO是基于对基类的递归调用。为了总结本节开头引用的Michele Simionato 的文章,将 C3 符号应用到我们的示例中,如下所示:

      L[MyClass(Base1, Base2)] =

              MyClass + merge(L[Base1], L[Base2], Base1, Base2)

这里 L[MyClass]是 MyClass 类的线性化,而 merge 是合并多个线性化结果的具体 算法。

因此,综合的描述应该是(正如 Simionato 所言):

“C 的线性化是 C 加上父类的线性化和父类列表的合并的总和。”

merge 算法负责删除重复项并保持正确的顺序。在文章中对该算法的描述为(根据我 们的例子做了适当修改):

“取第一个列表的表头(head),即 L[Base1][0]。如果这个表头不在其他 任何列表的表尾(tail),那么就将它添加到 Myclass 的线性化中,并从合并的 列表里删除;否则的话,查看下一个列表的表头,如果是一个好的表头就将其 取出。

然后重复这一操作,直到所有的类都被删除或者找不到好的表头为止。在 后一种情况下,无法构建合并,Python 2.3 将拒绝创建 MyClass 类,并引发一 个异常。”

head(表头)是列表的第一个元素,而 tail(表尾)则包含其余元素。例如,在(Base1,

Base2, ..., BaseN)中,Base1 是 head,而(Base2, ..., BaseN)则是 tail。 换句话说,C3 对每个父类进行递归深度查找以得到一个列表序列。然后,如果某个类 包含在多个列表中,它会利用层次结构消歧(hierarchy disambiguation)计算出从左到右的

规则,以此合并所有列表。

 其结果如下:

def L(klass):

return [k.__name__ for k in klass.__mro__]

      >>> L(MyClass)

      ['MyClass', 'Base1', 'Base2', 'CommonBase', 'object']

3.2 访问超类中的方法 

  类的__mro__属性(只读)保存了线性化的计算结果, 这在加载类定义时已经完成。

你也可以调用 MyClass.mro()来得到计算结果。这 也是对Python 2中的类应该区别对待的另一个原因。 虽然Python 2的旧式类已经定义了方法解析顺序,但 并没有提供__mro__属性和 mro()方法。因此,尽管 旧式类有解析顺序,但不能说它们有 MRO。在多数情 况下,如果某人提到 Python 中的 MRO,指的都是本节 介绍的 C3 算法。