今天,我读了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__
时发生的事情是:
type
是否为True。super
的{{1}}属性,isinstance(instance, type)
的{{1}}属性。super(Class, instance)
元组中传递给isinstance(instance, Class)
的{{1}}索引。 __class__
元组中的相应类,并返回该相应类的超级委托。 instance
长度,则返回step2的instance.__class__
中的最后一个类的委托,即__mro__
类。我的理解是对的吗?
如果我错了,Class
与super
的{{1}}互动的正确机制是什么?
如果我是对的,我应该如何为python官方文档修改提出问题?
因为我认为关于这个项目的当前版本可能会产生误导。
PS:此测试由__mro__
完成。
答案 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链中的下一个类实现,则搜索将向前跳过,直到找到具有已定义方法的类。