今天,我阅读了 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)
时发生了什么是:
- Python 检查是否
isinstance(instance, Class)
是真的。 - Python 找到
__class__
instance
的属性,
得到instance.__class__
的__mro__
属性。 - Python 找到
Class
的索引你传给了super
在__mro__
步骤 2 中的元组。 - Python将step3的索引加1,用它得到
__mro__
中对应的类步骤 2 的元组,并返回相应类的 super 委托(delegate)。 - 如果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/