`super` 如何与多重继承中的 class 的 `__mro__` 属性交互?

How does `super` interacts with a class's `__mro__` attribute in multiple inheritance?

今天,我读了official doc of super
其中提到多重继承将由 class.
__mro__ 属性决定 所以我做了一个小实验,结果让我很吃惊。

# CODE PART
class GrandFather(object):
    def p(self):
        print "I'm old."

class Father(GrandFather):
    def p(self):
        print "I'm male."

class Mother(object):
    def p(self):
        print "I'm female."

class Son(Father, Mother):
    def p(self):
        print "busy, busy, crwaling. "


 # EXPERIMENT PART
In [1]: Son.__mro__
Out[1]: (__main__.Son, __main__.Father, __main__.GrandFather, __main__.Mother, object)

In [2]: Father.__mro__
Out[2]: (__main__.Father, __main__.GrandFather, object)

In [3]: Mother.__mro__
Out[3]: (__main__.Mother, object)

In [4]: GrandFather.__mro__
Out[4]: (__main__.GrandFather, object)

In [5]: s = Son()

In [6]: super(Son, s).p()
I'm male.

In [7]: super(Father, s).p()
I'm old.

In [8]: super(Mother, s).p()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-ce4d0d6ef62d> in <module>()
----> 1 super(Mother, s).p()

AttributeError: 'super' object has no attribute 'p'

In [9]: super(GrandFather, s).p()
I'm female.

下面是我上面提到的官方文档的一部分,它说:

super(type[, object-or-type])
Return a proxy object that delegates method calls to a parent or sibling class of type.   
This is useful for accessing inherited methods that have been overridden in a class.  
The search order is same as that used by getattr() except that the type itself is skipped.

The __mro__ attribute of the type lists the method resolution search order  
used by both getattr() and super().   
The attribute is dynamic and can change whenever the inheritance hierarchy is updated.

If the second argument is an object, isinstance(obj, type) must be true.

结合本文档和我的实验结果。最令人困惑的部分是,当使用 super(GrandFather, s).p() 调用时,它调用 Motherp(),但 Mother 不在 GrandFather 的 [=14= 中],它的排名非常次于 Son__mro__

琢磨了一下。我得到了一个似是而非的解释,表明官方文档的不完整或不足:
也就是说,当与 super(type, instance) 一起使用时,super 函数将从 class__mro__ 属性中搜索 instance 的构建者,而不是 __mro__ 您传递给 supertype 属性,即使它满足 isinstance(instance, type) 条件。

那么当您输入 super(Class, instance) 时发生的事情是:

  1. Python 检查 isinstance(instance, Class) 是否为真。
  2. Python找到instance__class__属性,
    获取 instance.__class____mro__ 属性。
  3. Python 在第 2 步的 __mro__ 元组中找到您传递给 superClass 的索引。
  4. Python将step3的索引加1,用它得到step 2的__mro__元组中对应的class,以及return的super delegate这个对应class。
  5. 如果step4中的索引超过了step2的__mro__的长度,则step2的__mro__中最后一个class的delegate被returned,也就是object class.

我的理解对吗?
如果我错了,supertype__mro__ 交互的正确机制是什么?
如果我是对的,我应该如何为 python 官方文档修改提出问题?
因为我认为有关此项目的当前版本可能具有误导性。


PS:本次测试由Python 2.7.6 within IPython 3.2.1完成。

查看Son__mro__:

__main__.Son, __main__.Father, __main__.GrandFather, __main__.Mother, object

根据文档:

The __mro__ attribute of the type lists the method resolution search order

因此方法将根据 __mro__ 列表中的顺序从左到右进行搜索。调用 super(type, instance) 会将起始位置更改为指定为 super() 的第一个参数的类型,该类型在指定为第二个参数的实例的 class 的 __mro__ 列表中(如果传递给 super 的第二个参数是一个实例):

super(Son, s) 将代理到 __main__.Father

super(Father, s) 将代理到 __main__.GrandFather

super(GrandFather, s) 将代理到 __main__.Mother

super(Mother, s) 将代理到 object

有趣的是为什么 Son 中的 __mro__ 是这个样子。换句话说,为什么妈妈追着爷爷。这是因为线性化在 python:

中的工作方式

the linearization of C is the sum of C plus the merge of the linearizations of the parents and the list of the parents.

请参阅您提到的 documentation 中的示例,它解释了一个非常相似的案例。

因此最终结果实际上是正确的:super(GrandFather, s).p() 应该是 I'm female.

来自learning python的第32章:

each super call selects the method from a next class following it in the MRO ordering of the class of the self subject of a method call.

so for super(cls, instance)(isinstance(instance, cls) must be True),方法是从instance.__class__.__mro__中的下一个class开始选择[=14] =].

for super(cls0, cls1)(issubclass(cls1, cls0) must be True),方法是从cls0开始的cls1.__mro__中选择next class

在这两种情况下,如果方法未被 MRO 链中的下一个 class 实现,则搜索将向前跳过,直到找到具有已定义方法的 class。