我在这里看了关于python的super()方法的其他问题,但我仍然觉得很难理解整个概念。
我也在看pro python一书中的例子
那里引用的例子是
class A(object):
def test(self):
return 'A'
class B(A):
def test(self):
return 'B-->' + super(B, self).test()
class C(A):
def test(self):
return 'C'
class D(B, C):
pass
>>> A().test()
'A'
>>> B().test()
'B-->A'
>>> C().test()
'C'
>>> D().test()
'B-->C'
>>> A.__mro__
(__main__.A, object)
>>> B.__mro__
(__main__.B, __main__.A, object)
>>> C.__mro__
(__main__.C, __main__.A, object)
>>> D.__mro__
(__main__.D, __main__.B, __main__.C, __main__.A, object)
为什么要做D()。test()我们得到输出为'B - > C'而不是'B - > A'
书中的解释是
在最常见的情况下,包括此处显示的用法,super()有两个参数:a class和该类的实例。正如我们在此处的示例所示,实例对象确定 哪个MRO将用于解析结果对象的任何属性。提供的类确定 该MRO的一个子集,因为super()仅使用在类之后发生的MRO中的那些条目 提供。
我仍然觉得解释有点难以理解。这可能是一个可能的重复,并且已经多次询问过与此类似的问题,但如果我对此有所了解,我可能会更好地理解其他问题。
Understanding Python super() with __init__() methods
What does 'super' do in Python?
答案 0 :(得分:5)
如果您想知道Python选择此特定MRO算法的原因,请在mailing list archives中进行讨论,并在The Python 2.3 Method Resolution Order中进行简要总结。
但实际上,它归结为:Python 2.2的方法解决方案在处理多重继承时被打破,并且任何人建议修复它的第一件事是从Dylan借用C3算法,没有人遇到任何问题它或建议更好的东西,因此Python使用C3。
如果您对C3对其他算法的一般优势(和劣势)更感兴趣......
BrenBarn和florquake的答案给出了这个问题的基础知识。来自Raymond Hettinger博客的Python's super() considered super!是一个更长,更详细的讨论,绝对值得一读。
A Monotonic Superclass Linearlization for Dylan是描述设计的原始论文。当然Dylan是一种与Python非常不同的语言,这是一篇学术论文,但其基本原理仍然相当不错。
最后,The Python 2.3 Method Resolution Order(上面链接的相同文档)对这些好处进行了一些讨论。
你需要学习很多关于替代品的知识,以及他们是如何以及不适合Python的,以便更进一步。或者,如果您想了解有关SO的更深入信息,您需要提出更具体的问题。
最后,如果您问的是“如何”问题:
当您致电D().test()
时,它显然会调用您在B
的{{1}}方法中定义的代码。 test
是B.__mro__
。那么,(__main__.B, __main__.A, object)
如何调用super(B, self).test()
的{{1}}方法代替C
呢?
这里的关键是MRO基于test
的类型,而不是基于定义A
方法的类型self
。如果您在B
函数内test
print(type(self))
,则会看到它是test
,而不是D
。
因此,B
实际获得super(B, self)
(在本例中为self.__class__.__mro__
),在列表中找到D.__mro__
,然后返回其后的内容。非常简单。
但这并没有解释MRO是如何运作的,只是它的作用。 B
如何从D().test()
调用方法,但B
是self
?
首先,请注意D
,D().test
和D.test
不是同一个函数,因为它们根本不是函数;他们是方法。 (我在这里假设Python 2.x。事情有点不同 - 主要是在3.x中更简单。)
方法基本上是具有B.test
,im_func
和im_class
成员的对象。当你调用一个方法时,你所做的就是调用它的im_self
,其im_func
(如果不是im_self
)在开始时作为额外的参数填入。
因此,我们的三个示例都具有相同的None
,其中 是您在im_func
中定义的函数。但前两个对B
有D
而不是B
,第一个对im_class
有D
个实例而不是None
。因此,调用它最终将im_self
实例作为D
传递。
那么,self
如何以D().test
和im_self
结束?这会在哪里创建?这是有趣的部分。有关完整说明,请阅读Descriptor HowTo Guide,但请简要说明一下:
每当你写im_class
时,实际发生的事情等同于对foo.bar
的调用,它会执行类似这样的操作(忽略实例属性,getattr(foo, 'bar')
,__getattr__
,插槽,builtins等):
__getattribute__
最后def getattr(obj, name):
for cls in obj.__class__.__mro__:
try:
desc = cls.__dict__[name]
except KeyError:
pass
else:
return desc.get(obj.__class__, obj)
是神奇的位。如果你看一个函数 - 比如.get()
,你会发现它实际上有一个B.test.im_func
方法。它的作用是创建一个绑定方法,get
本身,im_func
作为类im_class
,obj.__class__
作为对象im_self
。< / p>
答案 1 :(得分:4)
简短的回答是方法解析顺序大致为#34;广度优先&#34;。也就是说,在进入任何超类之前,它会遍历给定级别的祖先的所有基类。因此,如果D继承自B和C,它们都继承自A,那么MRO在A之前总是具有B 和 C.
另一种思考方式是,如果订单变为B-> A,那么A.test
将在C.test
之前被调用,即使C
是A
的子类1}}。您通常希望在超类之前调用子类实现(因为子类可能希望完全覆盖超类而根本不调用它)。
可以找到更长的解释here。您还可以通过Google搜索或搜索Stackoverflow以查找有关&#34; Python方法解析顺序&#34;的问题。或者&#34; Python MRO&#34;。
答案 2 :(得分:3)
super()
基本上就是你如何告诉Python“做这个对象的其他类所说的。”
当你的每个类只有一个父(单继承)时,super()
只会引用你的父类。 (我猜你已经理解了这一部分。)
但是当您使用多个基类时,就像您在示例中所做的那样,事情开始变得更加复杂。在这种情况下,Python确保如果你到处调用super()
,每个类的方法都会被调用。
(有点荒谬)的例子:
class Animal(object):
def make_sounds(self):
pass
class Duck(Animal):
def make_sounds(self):
print 'quack!'
super(Duck, self).make_sounds()
class Pig(Animal):
def make_sounds(self):
print 'oink!'
super(Pig, self).make_sounds()
# Let's try crossing some animals
class DuckPig(Duck, Pig):
pass
my_duck_pig = DuckPig()
my_duck_pig.make_sounds()
# quack!
# oink!
你希望你的DuckPig
说quack!
和oink!
,毕竟它是猪和鸭,对吧?嗯,这就是super()
的用途。