Python中super的应用
- 约定
- 单继承
- 多继承
- super 是个类
- 多继承中 super 的工作方式
- 参考资料
约定
在开始之前我们来约定一下本文所使用的 Python 版本。默认用的是 Python 3,也就是说:本文所定义的类都是新式类。如果你用到是 Python 2 的话,记得继承 object:
# 默认, Python 3
class A:
pass # Python 2
class A(object):
pass
Python 3 和 Python 2 的另一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
# 默认,Python 3
class B(A):
def add(self, x):
super().add(x) # Python 2
class B(A):
def add(self, x):
super(B, self).add(x)
所以,你如果用的是 Python 2 的话,记得将本文的 super() 替换为 suepr(Class, self) 。
如果还有其他不兼容 Python 2 的情况,我会在文中注明的。
单继承
在单继承中 super 就像大家所想的那样,主要是用来调用父类的方法的。
class A:
def __init__(self):
self.n = 2 def add(self, m):
print('self is {0} @A.add'.format(self))
self.n += m class B(A):
def __init__(self):
self.n = 3 def add(self, m):
print('self is {0} @B.add'.format(self))
super().add(m)
self.n += 3
你觉得执行下面代码后, b.n 的值是多少呢?
b = B()
b.add(2)
print(b.n)
执行结果如下:
self is <__main__.B object at 0x106c49b38> @B.add
self is <__main__.B object at 0x106c49b38> @A.add
8
这个结果说明了两个问题:
- super().add(m) 确实调用了父类 A 的 add 方法。
- super().add(m) 调用父类方法 def add(self, m) 时, 此时父类中 self 并不是父类的实例而是子类的实例, 所以b.add(2) 之后的结果是 5 而不是 4 。
不知道这个结果是否和你想到一样呢?下面我们来看一个多继承的例子。
多继承
这次我们再定义一个 class C,一个 class D:
class C(A):
def __init__(self):
self.n = 4 def add(self, m):
print('self is {0} @C.add'.format(self))
super().add(m)
self.n += 4 class D(B, C):
def __init__(self):
self.n = 5 def add(self, m):
print('self is {0} @D.add'.format(self))
super().add(m)
self.n += 5
下面的代码又输出啥呢?
d = D()
d.add(2)
print(d.n)
这次的输出如下:
self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19
你说对了吗?你可能会认为上面代码的输出类似:
self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @A.add
15
为什么会跟预期的不一样呢?下面我们将一起来看看 super 的奥秘。
super 是个类
当我们调用 super() 的时候,实际上是实例化了一个 super 类。你没看错, super 是个类,既不是关键字也不是函数等其他数据结构:
>>> class A: pass
...
>>> s = super(A)
>>> type(s)
<class 'super'>
>>>
在大多数情况下, super 包含了两个非常重要的信息: 一个 MRO 以及 MRO 中的一个类。当以如下方式调用 super 时:
super(a_type, obj)
MRO 指的是 type(obj) 的 MRO, MRO 中的那个类就是 a_type , 同时 isinstance(obj, a_type) == True 。
当这样调用时:
super(type1, type2)
MRO 指的是 type2 的 MRO, MRO 中的那个类就是 type1 ,同时 issubclass(type2, type1) == True 。
那么, super() 实际上做了啥呢?简单来说就是:提供一个 MRO 以及一个 MRO 中的类 C , super() 将返回一个从 MRO 中 C 之后的类中查找方法的对象。
也就是说,查找方式时不是像常规方法一样从所有的 MRO 类中查找,而是从 MRO 的 tail 中查找。
举个栗子, 有个 MRO:
[A, B, C, D, E, object]
下面的调用:
super(C, A).foo()
super 只会从 C 之后查找,即: 只会在 D 或 E 或 object 中查找 foo 方法。
多继承中 super 的工作方式
再回到前面的
d = D()
d.add(2)
print(d.n)
现在你可能已经有点眉目,为什么输出会是
self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19
了吧 ;)
下面我们来具体分析一下:
- D 的 MRO 是: [D, B, C, A, object] 。 备注: 可以通过 D.mro() (Python 2 使用 D.__mro__ ) 来查看 D 的 MRO 信息)
- 详细的代码分析如下:
class A:
def __init__(self):
self.n = 2 def add(self, m):
# 第四步
# 来自 D.add 中的 super
# self == d, self.n == d.n == 5
print('self is {0} @A.add'.format(self))
self.n += m
# d.n == 7 class B(A):
def __init__(self):
self.n = 3 def add(self, m):
# 第二步
# 来自 D.add 中的 super
# self == d, self.n == d.n == 5
print('self is {0} @B.add'.format(self))
# 等价于 suepr(B, self).add(m)
# self 的 MRO 是 [D, B, C, A, object]
# 从 B 之后的 [C, A, object] 中查找 add 方法
super().add(m) # 第六步
# d.n = 11
self.n += 3
# d.n = 14 class C(A):
def __init__(self):
self.n = 4 def add(self, m):
# 第三步
# 来自 B.add 中的 super
# self == d, self.n == d.n == 5
print('self is {0} @C.add'.format(self))
# 等价于 suepr(C, self).add(m)
# self 的 MRO 是 [D, B, C, A, object]
# 从 C 之后的 [A, object] 中查找 add 方法
super().add(m) # 第五步
# d.n = 7
self.n += 4
# d.n = 11 class D(B, C):
def __init__(self):
self.n = 5 def add(self, m):
# 第一步
print('self is {0} @D.add'.format(self))
# 等价于 super(D, self).add(m)
# self 的 MRO 是 [D, B, C, A, object]
# 从 D 之后的 [B, C, A, object] 中查找 add 方法
super().add(m) # 第七步
# d.n = 14
self.n += 5
# self.n = 19 d = D()
d.add(2)
print(d.n)
调用过程图如下:
D.mro() == [D, B, C, A, object]
d = D()
d.n == 5
d.add(2) class D(B, C): class B(A): class C(A): class A:
def add(self, m): def add(self, m): def add(self, m): def add(self, m):
super().add(m) 1.---> super().add(m) 2.---> super().add(m) 3.---> self.n += m
self.n += 5 <------6. self.n += 3 <----5. self.n += 4 <----4. <--|
(14+5=19) (11+3=14) (7+4=11) (5+2=7)
现在你知道为什么 d.add(2) 后 d.n 的值是 19 了吧 ;
Python中super的应用的更多相关文章
- python中super().__init__和类名.__init__的区别
super().__init__相对于类名.__init__,在单继承上用法基本无差 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次 多继承时 ...
- Python中super的用法【转载】
Python中super的用法[转载] 转载dxk_093812 最后发布于2019-02-17 20:12:18 阅读数 1143 收藏 展开 转载自 Python面向对象中super用法与MRO ...
- Python中super函数的用法
之前看python文档的时候发现许多单继承类也用了super()来申明父类,那么这样做有何意义? 从python官网文档对于super的介绍来看,其作用为返回一个代理对象作为代表调用父类或亲类方法.( ...
- python中super()的一些用法
在看python高级编程这本书的时候,在讲到super的时候,产生了一些疑惑,super在python中的用法跟其他的语言有一些不一样的地方,在网上找了一些资料,发现基本上很少有文章能把我的疑惑讲明白 ...
- python中super的理解(转)
原文地址:https://www.zhihu.com/question/20040039 针对你的问题,答案是可以,并没有区别.但是这题下的回答我感觉都不够好. 要谈论 super,首先我们应该无视 ...
- Python中super()的用法
参考链接:https://www.cnblogs.com/shengulong/p/7892266.html super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是 ...
- Python中super详解
转至:https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html 说到 super, 大家可能觉得很 ...
- python中super的使用方法
说白了,super的使用就是要子类要调用父类的方法,我们就用super,那你要有调用的规范,我们明白这个规范就可以了. 在python2和python3中,调用方法不同,注意就是了.Python3.x ...
- python中super的使用
转自:http://python.jobbole.com/86787/ super() 的入门使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能, ...
随机推荐
- 牛客练习赛23 F 托米的游戏
https://www.nowcoder.com/acm/contest/156/F 树 概率 #include <cstdio> #include <cstdlib> #in ...
- eclipse 支持 Robot framework 编辑环境
一.配置python 环境 1. 设置pydev库 打开Help -> Install New Software, 点击'Add',设置Name为“Pydev”,Loca ...
- laravel mapSpread 例子
$collection = collect(range(1, 9)); $chunks = $collection->chunk(2); $labeld = $chunks->mapSpr ...
- R读取excel文件
2017.09.05 我一个下午的成果啊啊啊啊,看看失败 不禁感叹一声,失败的路真是多啊!!!! 一.安装xlsx包 下面具体讲一讲怎么弄的(太笨了,所以学得慢,需要一步一步的来) 用R读取excel ...
- poj 2774 后缀数组 两个字符串的最长公共子串
Long Long Message Time Limit: 4000MS Memory Limit: 131072K Total Submissions: 31904 Accepted: 12 ...
- ngx_lua_API 指令详解(一)ngx.timer.at 指令
语法: ok,err = ngx.timer.at(delay,callback,user_arg1,user_arg2 ...) 上下文: init_worker_by_lua *,set_by_l ...
- 学以致用:Python爬取廖大Python教程制作pdf
当我学了廖大的Python教程后,感觉总得做点什么,正好自己想随时查阅,于是就开始有了制作PDF这个想法. 想要把教程变成PDF有三步: 先生成空html,爬取每一篇教程放进一个新生成的div,这样就 ...
- Linux IO调度算法
Linux IO调度算法 操作系统的调度 CPU调度 CPU scheduler IO调度 IO scheduler IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移 ...
- 61.volatile关键字
volatile作用 volatile的作用是可以保持共享变量的可见性,即一个线程修改一个共享变量后,另一个线程能够读取到这个修改后的值. 先来看一个问题: 定义一个Task类 package com ...
- fonts.googleapis.com 字体报错问题解决。
更多内容推荐微信公众号,欢迎关注: 无法加载这些字体是因为,google的网站在国内无法访问造成的.在AdminLTE.css 和AdminLte.less中有如下内容: @import url(ht ...