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

时间:2015-11-24 10:16:29

标签: 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的{​​{1}}非常低的顺序。

经过一番思考之后。我得到了一个似是而非的解释,表明官方文件的不完整性或不足之处:
这与__mro__一起使用时,super(type, instance)函数将从您super构建的__mro__的{​​{1}}属性进行搜索,而不是{您传递给class的{​​{1}}的{​​1}}属性,即使它满足instance条件。

因此,当您键入__mro__时发生的事情是:

  1. Python检查type是否为True。
  2. Python找到super的{​​{1}}属性,
    获取isinstance(instance, type)的{​​{1}}属性。
  3. Python在步骤2中找到您在super(Class, instance)元组中传递给isinstance(instance, Class)的{​​{1}}索引。
  4. Python将step3的索引加1,用它来获取步骤2的__class__元组中的相应类,并返回该相应类的超级委托。
  5. 如果step4中的索引超过step2的instance长度,则返回step2的instance.__class__中的最后一个类的委托,即__mro__类。
  6. 我的理解是对的吗? 如果我错了,Classsuper的{​​{1}}互动的正确机制是什么? 如果我是对的,我应该如何为python官方文档修改提出问题? 因为我认为关于这个项目的当前版本可能会产生误导。

    PS:此测试由__mro__完成。

2 个答案:

答案 0 :(得分:2)

查看__mro__的{​​{1}}:

Son

根据文件:

  

该类型的__main__.Son, __main__.Father, __main__.GrandFather, __main__.Mother, object 属性列出方法解析搜索顺序

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

__mro__将代理super(Son, s)

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

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

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

有趣的部分是object __mro__的原因。换句话说,为什么母亲在追随GrandFather。这是因为线性化在python中是如何工作的:

  

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

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

因此,最终结果实际上是正确的:Son应为super(GrandFather, s).p()

答案 1 :(得分:0)

learning python的第32章开始:

  

每个超级调用在方法调用的自我主题的类的MRO排序中从它后面的下一个类中选择方法。

因此对于super(cls, instance)isinstance(instance, cls)必须为True),从instance.__class__.__mro__开始,instance.__class__中的下一个班级会选择该方法。

super(cls0, cls1)issubclass(cls1, cls0)必须为True),从cls1.__mro__

开始,cls0中的下一个班级选择该方法

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