python - `super` 在多重继承中如何与类的 `__mro__` 属性交互?

标签 python inheritance superclass super

今天,我阅读了 official doc of super .
其中提到多重继承将由__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() 调用时它调用 p()Mother ,但是Mother不在 GrandFather 中的 __mro__ ,它的顺序非常低Son__mro__ .

经过一番琢磨。我得到了一个似是而非的解释,表明官方文档的不完整或不足:
那是与 super(type, instance) 一起使用时, super函数将从 __mro__ 开始搜索class 的属性来自谁你的instance是构建,但不是 __mro__ type 的属性你传给了super ,即使它满足 isinstance(instance, type)条件。

那么当你输入 super(Class, instance) 时发生了什么是:

  1. Python 检查是否 isinstance(instance, Class)是真的。
  2. Python 找到 __class__ instance 的属性,
    得到 instance.__class____mro__属性。
  3. Python 找到 Class 的索引你传给了super__mro__步骤 2 中的元组。
  4. Python将step3的索引加1,用它得到__mro__中对应的类步骤 2 的元组,并返回相应类的 super 委托(delegate)。
  5. 如果step4中的索引超过__mro__的长度step2,最后一节课的代表__mro__ step2 的返回,即 object类。

我的理解对吗?
如果我错了,super 的正确机制是什么?与 type 互动的 __mro__ ?
如果我是对的,python官方文档修改应该怎么提issue?
因为我认为有关此项目的当前版本可能具有误导性。


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) 会将起始位置更改为 __mro__ 列表中 super() 的第一个参数指定的类型指定为第二个参数的实例的类(如果传递给 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.

关于python - `super` 在多重继承中如何与类的 `__mro__` 属性交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33890918/

相关文章:

python - "Least Astonishment"和可变默认参数

python - 在python的同一行中分配给多个变量

python - 在 Matplotlib 中更改网格间隔并指定刻度标签

c++ - 非虚平凡析构函数+继承

java - 为什么 Spring @Configuration 类继承没有按预期工作?

ios - 为什么 "[self class] == [super class]"?

java - 如何在子类的构造函数中实现父类(super class)的数组列表?

Python 正则表达式不匹配,但适用于正则表达式测试器

c++ - 重载函数模板和继承参数

java - 继承 :hidden variable of superclass in subclass