http://funhacks.net/2016/11/09/super/

super仅被用于新式类

在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现,比如:

 class Animal(object):
def __init__(self, name):
self.name = name
def greet(self):
print 'Hello, I am %s.' % self.name
class Dog(Animal):
def greet(self):
super(Dog, self).greet() # Python3 在类内部可使用 super().greet()
print 'WangWang...'

在上面,Animal 是父类,Dog 是子类,我们在 Dog 类重定义了 greet 方法,为了能同时实现父类的功能,我们又调用了父类的方法,看下面的使用:

>>> dog = Dog('dog')

>>> dog.greet()

Hello, I am dog.

WangWang..

super 的一个最常见用法可以说是在子类中调用父类的初始化方法了,比如:

 class Base(object):
def __init__(self, a, b):
self.a = a
self.b = b
class A(Base):
def __init__(self, a, b, c):
super(A, self).__init__(a, b) # Python3 可使用 super().__init__(a, b)
self.c = c

深入 super()

看了上面的使用,你可能会觉得 super 的使用很简单,无非就是获取了父类,并调用父类的方法。其实,在上面的情况下,super 获得的类刚好是父类,但在其他情况就不一定了,super 其实和父类没有实质性的关联。

让我们看一个稍微复杂的例子,涉及到多重继承,代码如下:

class Base(object):
def __init__(self):
print "enter Base"
print "leave Base"
class A(Base):
def __init__(self):
print "enter A"
super(A, self).__init__()
print "leave A"
class B(Base):
def __init__(self):
print "enter B"
super(B, self).__init__()
print "leave B"
class C(A, B):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C"

其中,Base 是父类,A, B 继承自 Base, C 继承自 A, B,它们的继承关系如下:

Base

/  \

/    \

A      B

\    /

\  /

C

现在,让我们看一下使用:

>>> c = C()

enter C

enter A

enter B

enter Base

leave Base

leave B

leave A

leave C

如果你认为 super 代表『调用父类的方法』,那你很可能会疑惑为什么 enter A 的下一句不是 enter Base 而是 enter B。原因是,super 和父类没有实质性的关联,现在让我们搞清 super 是怎么运作的。

MRO 列表

事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表:

>>> C.mro()   # or C.__mro__ or C().__class__.mro()

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法来实现的

super 原理

super 的工作原理如下(不是很精确,事实上super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,产生了一个super对象,而这个对象接近于实例self,所以super(B,self).__init__是实例调用方法,为绑定方法)

class Animal(object):
def __init__(self, name):
self.name = name
def greet(self):
print ('Hello, I am %s.' % self.name)
class Dog(Animal):
def greet(self):
print (super(Dog,self).greet) a=Dog('aa')
a.greet()#<bound method Animal.greet of <__main__.Dog object at 0x7f898d22d550>>

def super(cls, inst):

mro = inst.__class__.mro()

return mro[mro.index(cls) + 1]

其中,cls 代表类,inst 代表实例,上面的代码做了两件事:

获取 inst 的 MRO 列表

查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]

当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。

现在,让我们回到前面的例子。

首先看类 C 的 __init__ 方法:

super(C, self).__init__()

这里的 self 是当前 C 的实例,self.class.mro() 结果是:

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

可以看到,C 的下一个类是 A,于是,跳到了 A 的 __init__,这时会打印出 enter A,并执行下面一行代码:

super(A, self).__init__()

注意,这里的 self 也是当前 C 的实例,MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个类,发现是 B,于是,跳到了 B 的 __init__,这时会打印出 enter B,而不是 enter Base。

整个过程还是比较清晰的,关键是要理解 super 的工作方式,而不是想当然地认为 super 调用了父类的方法。

小结

事实上,super 和父类没有实质性的关联。

super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类

再说一个容易错的点:

c=4
class a:
c=2
def __init__(self):
print (c) aa=a()#打印是4,而不是2.如果把全局变量的c注释掉,就是第一行注释掉,则报错

转载 你不知道的super的更多相关文章

  1. Python: 你不知道的 super

    https://segmentfault.com/a/1190000007426467 Python: 你不知道的 super 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我 ...

  2. [转载]Python: 你不知道的 super

    原文出处: geekvi super() 的入门使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用  ...

  3. (转载)深入super,看Python如何解决钻石继承难题

    1.   Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object): def __init_ ...

  4. 你真的理解Java的this和super吗?

    你不知道的this 很多介绍java的书籍都说this指该对象本身.我们来看下面代码: class Base{ private int i = 3; public Base() { this.disp ...

  5. super 使用以及原理

    用super也很久了,但是一直没有关注过他的原理.最近开始越来越多关注python更底层的实现和奇技淫巧.看到该方法越发使用得多所以也研究了一波 平时单继承可能是我们遇到最多的情况.无非就是类似情况. ...

  6. 关于super的理解,三篇文章支撑起来的

    1.有向图的拓扑排序https://blog.csdn.net/wp1603710463/article/details/50900892 2. Python中MRO算法 http://mp.weix ...

  7. Python零散函数

    1. python json.dumps() json.dump()的区别 注意cat ,是直接输出文件的内容 load和loads都是实现"反序列化",区别在于(以Python为 ...

  8. [PY3]——threading.Event

    Class Event { __init__(self) clear(self) is_set(self) set(self) wait(self,timeout=None) } is_set(sel ...

  9. ATTENTION NETWORK分析

    1. TensorFlowTrainable类 1 class TensorFlowTrainable(object): 2 def __init__(self): 3 self.parameters ...

随机推荐

  1. PAT甲题题解-1117. Eddington Number(25)-(大么个大水题~)

    如题,大水题...贴个代码完事,就这么任性~~ #include <iostream> #include <cstdio> #include <algorithm> ...

  2. Linux第三周学习总结——构造一个简单的Linux系统MenuOS

    第三周学习总结--构造一个简单的Linux系统MenuOS 作者:刘浩晨 [原创作品转载请注明出处] <Linux内核分析>MOOC课程http://mooc.study.163.com/ ...

  3. [福大软工] Z班 团队作业——UML设计 作业成绩

    团队作业--UML设计 作业链接 http://www.cnblogs.com/easteast/p/7745703.html 作业要求 1)团队分工(5分) 描述团队的每个成员分别完成了UML图的哪 ...

  4. 对比网络模拟器软件——Cisco Packet Tracer、华为eNSP、H3C Cloud Lab

    1.软件介绍 1.1 Cisco Packet Tracer Cisco Packet Tracer(以下简称PT)是一款由思科公司开发的,为网络课程的初学者提供辅助教学的实验模拟器.使用者可以在该模 ...

  5. 【壹拾壹周】final分数分配

    组名: 新蜂 组长: 武志远 组员: 宫成荣 谢孝淼 杨柳 李峤 项目名称: java俄罗斯方块NEO 发布时间:12.3 git地址:https://git.coding.net/Boxer_/ho ...

  6. 第十一周(11.24-12.01)----WBS功能分解

    功能 子功能 二级子功能 预计花费时间(小时) 游戏基础功能 显示首界面 绘制产产品主logo及不同难度下的布局 4   游戏 难度选择(初级.中级.高级) 4     退出整个程序 1     放弃 ...

  7. node解决跨域问题

    app.all('*', function(req, res, next) { res.header("Access-Control-Allow-Origin", "*& ...

  8. java自定义注解学习(一)_demo小练习

    自定义注解 现在大家开发过程中,经常会用到注解. 比如@Controller 等等,但是有时候也会碰到自定义注解,在开发中公司的记录日志就用到了自定义注解.身为渣渣猿还是有必要学习下自定义注解的. 这 ...

  9. CAS单点登录的时候出现票根'ST-xxxxxx-cas'不符合目标服务

    CAS单点登录遇到问题:票根'ST-xxxxxx-cas'不符合目标服务,原因出在linux 时间未同步,差了3分钟 .

  10. 【刷题】BZOJ 1124 [POI2008]枪战Maf

    Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的人也不同. ...