`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()
调用时,它调用 Mother
的 p()
,但 Mother
不在 GrandFather
的 [=14= 中],它的排名非常次于 Son
的 __mro__
。
琢磨了一下。我得到了一个似是而非的解释,表明官方文档的不完整或不足:
也就是说,当与 super(type, instance)
一起使用时,super
函数将从 class
的 __mro__
属性中搜索 instance
的构建者,而不是 __mro__
您传递给 super
的 type
属性,即使它满足 isinstance(instance, type)
条件。
那么当您输入 super(Class, instance)
时发生的事情是:
- Python 检查
isinstance(instance, Class)
是否为真。
- Python找到
instance
的__class__
属性,
获取 instance.__class__
的 __mro__
属性。
- Python 在第 2 步的
__mro__
元组中找到您传递给 super
的 Class
的索引。
- Python将step3的索引加1,用它得到step 2的
__mro__
元组中对应的class,以及return的super delegate这个对应class。
- 如果step4中的索引超过了step2的
__mro__
的长度,则step2的__mro__
中最后一个class的delegate被returned,也就是object
class.
我的理解对吗?
如果我错了,super
与 type
的 __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。
今天,我读了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()
调用时,它调用 Mother
的 p()
,但 Mother
不在 GrandFather
的 [=14= 中],它的排名非常次于 Son
的 __mro__
。
琢磨了一下。我得到了一个似是而非的解释,表明官方文档的不完整或不足:
也就是说,当与 super(type, instance)
一起使用时,super
函数将从 class
的 __mro__
属性中搜索 instance
的构建者,而不是 __mro__
您传递给 super
的 type
属性,即使它满足 isinstance(instance, type)
条件。
那么当您输入 super(Class, instance)
时发生的事情是:
- Python 检查
isinstance(instance, Class)
是否为真。 - Python找到
instance
的__class__
属性,
获取instance.__class__
的__mro__
属性。 - Python 在第 2 步的
__mro__
元组中找到您传递给super
的Class
的索引。 - Python将step3的索引加1,用它得到step 2的
__mro__
元组中对应的class,以及return的super delegate这个对应class。 - 如果step4中的索引超过了step2的
__mro__
的长度,则step2的__mro__
中最后一个class的delegate被returned,也就是object
class.
我的理解对吗?
如果我错了,super
与 type
的 __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。