python封装函数讲解(Python中super函数用法实例分析)
python封装函数讲解
Python中super函数用法实例分析本文实例讲述了Python中super函数用法。分享给大家供大家参考,具体如下:
这是个高大上的函数,在python装13手册里面介绍过多使用可显得自己是高手 23333. 但其实他还是很重要的. 简单说, super
函数是调用下一个父类(超类)并返回该父类实例的方法. 这里的下一个的概念参考后面的MRO表介绍.
help介绍如下:
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type) -> unbound super object
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
def meth(self, arg):
super(C, self).meth(arg)
由此可知, super有三种用法, 第一参数总是召唤父类的那个类, 第二参数可缺(返回非绑定父类对象),也可以是实例对象或该类的子类. 最终返回的都是父类的实例(绑定或非绑定). 在Python3中,super函数多了一种用法是直接super()
,相当于super(type,首参)
, 这个首参就是一般的传入的self
实例本身啦. 因为在py2里面常用也是这种写法.
另外, 在py2中, super只支持新类( new-style class, 就是继承自object的).
为什么要调用父类?
在类继承时, 要是重定义某个方法, 这个方法就会覆盖掉父类的相应同名方法. 通过调用父类实例, 可以在子类中同时实现父类的功能.例如:
|
# Should be new-class based on object in python2. class A( object ): def __init__( self ): print "enter A" print "leave A" class B(A): def __init__( self ): print "enter B" super (B, self ).__init__() print "leave B" >>> b = B() enter B enter A leave A leave B |
通过调用super
获得父类实例从而可以实现该实例的初始化函数. 这在实践中太常用了 (因为要继承父类的功能, 又要有新的功能).
直接使用父类来调用的差异
事实上, 上面的super
函数方法还可以这么写:
|
class A( object ): def __init__( self ): print "enter A" print "leave A" class B(A): def __init__( self ): print "enter B" A.__init__( self ) print "leave B" |
通过直接使用父类类名来调用父类的方法, 实际也是可行的. 起码在上面的例子中效果上他们现在是一样的. 这种方法在老式类中也是唯一的调用父类的方法 (老式类没有super).
通过父类类名调用方法很常用, 比较直观. 但其效果和super还是有差异的. 例如:
|
class A( object ): def __init__( self ): print "enter A" print "leave A" class B(A): def __init__( self ): print "enter B" A.__init__( self ) print "leave B" class C(A): def __init__( self ): print "enter C" A.__init__( self ) print "leave C" class D(B,C): def __init__( self ): print "enter D" B.__init__( self ) C.__init__( self ) print "leave D" >>> d = D() enter D enter B enter A leave A leave B enter C enter A leave A leave C leave D |
可以发现, 这里面A的初始化函数被执行了两次. 因为我们同时要实现B和C的初始化函数, 所以分开调用两次, 这是必然的结果.
但如果改写成super呢?
|
class A( object ): def __init__( self ): print "enter A" print "leave A" class B(A): def __init__( self ): print "enter B" super (B, self ).__init__() print "leave B" class C(A): def __init__( self ): print "enter C" super (C, self ).__init__() print "leave C" class D(B,C): def __init__( self ): print "enter D" super (D, self ).__init__() print "leave D" >>> d = D() enter D enter B enter C enter A leave A leave C leave B leave D |
会发现所有父类ABC只执行了一次, 并不像之前那样执行了两次A的初始化.
然后, 又发现一个很奇怪的: 父类的执行是 BCA 的顺序并且是全进入后再统一出去. 这是MRO表问题, 后面继续讨论.
如果没有多继承, super
其实和通过父类来调用方法差不多. 但, super还有个好处: 当B继承自A, 写成了A.__init__
, 如果根据需要进行重构全部要改成继承自 E
,那么全部都得改一次! 这样很麻烦而且容易出错! 而使用super()
就不用一个一个改了(只需类定义中改一改就好了)
Anyway, 可以发现, super
并不是那么简单.
MRO 表
MRO是什么? 可以通过以下方式调出来:
|
>>> D.mro() # or d.__class__.mro() or D.__class__.mro(D) [D, B, C, A, object ] >>> B.mro() [B, A, object ] >>> help (D.mro) #Docstring: #mro() -> list #return a type's method resolution order #Type: method_descriptor |
MRO就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表 (类继承顺序表去理解也行) 啦.
这个表有啥用? 首先了解实际super做了啥:
|
def super ( cls , inst): mro = inst.__class__.mro() return mro[mro.index( cls ) + 1 ] |
换而言之, super方法实际是调用了cls的在MRO表中的下一个类. 如果是简单一条线的单继承, 那就是父类->父类一个一个地下去罗. 但对于多继承, 就要遵循MRO表中的顺序了. 以上面的D的调用为例:
d的初始化
-> D (进入D) super(D,self)
-> 父类B (进入B) super(B,self)
-> 父类C (进入C) super(C,self)
-> 父父类A (进入A) (退出A) # 如有继续super(A,self) -> object (停了)
-> (退出C)
-> (退出B)
-> (退出D)
所以, 在MRO表中的超类初始化函数只执行了一次!
那么, MRO的顺序究竟是怎么定的呢? 这个可以参考官方说明The Python 2.3 Method Resolution Order . 基本就是, 计算出每个类(从父类到子类的顺序)的MRO, 再merge 成一条线. 遵循以下规则:
在 MRO 中,基类永远出现在派生类后面,如果有多个基类,基类的相对顺序保持不变。 这个原则包括两点:
1. 基类永远在派生类后面
2. 类定义时的继承顺序影响相对顺序.
如果有以下继承 (Python中的super用法详解):
object
/ \
/ A
| / \
B-1 C-2 D-2
\ / /
E-1 /
\ /
F
那么MRO是: F -> E -> B -> C -> D -> A -> object
怎么解释呢?
根据官方的方法, 是:
|
L(O) = O L(B) = B O L(A) = A O L(C) = C A O L(D) = D A O L(E) = E + merge(L(B),L(C)) = E + merge(BO,CAO) = E + B + merge(O,CAO) = E + B + C + merge(O,AO) = E + B + C + A + merge(O,O) = E B C A O L(F) = F + merge(L(E),L(D)) = F + merge(EBCAO,DAO) = F + EBC + merge(AO,DAO) = F + EBC + D + merge(AO,AO) = F EBC D AO |
看起来很复杂..但还是遵循在 MRO 中,基类永远出现在派生类后面,如果有多个基类,基类的相对顺序保持不变。所以, 我个人认为可以这么想:
-
先找出最长深度最深的继承路线
F->E->C->A->object
. (因为必然基类永远出现在派生类后面) -
类似深度优先, 定出其余顺序:
F->E->B->obj
,F->D->A-object
- 如果有多个基类,基类的相对顺序保持不变, 类似于merge时优先提前面的项. 所以排好这些路线: (FEBO, FECAO, FDAO)
-
F->E->B->obj
且E(B,C)决定B在C前面.所以F->E->B->C->A->obj
(相当于F+merge(EBO,ECAO)
). -
F->D->A-object
且F(E,D)决定了D在E后, 所以D在E后A前. 因为相对顺序, 相当于FE+merge(BCAO, DAO)
, 所以FE BC D AO
更多可参考
- Raymond Hettinger 的Python's super() considered super! (据说很经典的讨论)
- James Knight 的 Python's Super Considered Harmful
- Py3 cookbook: 8.7 调用父类方法http://python3-cookbook.readthedocs.org/zh_CN/latest/c08/p07_calling_method_on_parent_class.html
- Python编程中对super函数的正确理解和用法解析
希望本文所述对大家Python程序设计有所帮助。
原文链接:http://gohom.win/2016/02/23/py-super/
- python 多线程与多进程(python 多线程串行和并行的实例)
- python测试websocket接口(Python如何爬取实时变化的WebSocket数据的方法)
- Python实现模拟点击(用python实现刷点击率的示例代码)
- python图文生成器(Python生成器的使用方法和示例代码)
- python飞机大战游戏背景(python实现飞机大战游戏)
- anaconda如何配置python3.7(WIn10+Anaconda环境下安装PyTorch避坑指南)
- python对于gbk处理(Python 2/3下处理cjk编码的zip文件的方法)
- 卷积神经网络python实现(Python通过TensorFlow卷积神经网络实现猫狗识别)
- pythonfor循环嵌套if语句(Python基础教程之if判断,while循环,循环嵌套)
- python参模块使用教程(Python参数解析模块sys、getopt、argparse使用与对比分析)
- python的log函数(Python3 log10函数简单用法)
- python如何遍历列表并提取(Python同步遍历多个列表的示例)
- python面向对象基本思想(详解Python:面向对象编程)
- pyqt5怎么在pycharm中安装(pycharm+PyQt5+python最新开发环境配置踩坑)
- python实时输出图像(Python给图像添加噪声具体操作)
- python html文字分段(Python对HTML转义字符进行反转义的实现方法)
- ()
- 百事大吉蓝底 绿底手机高清壁纸(绿底手机高清壁纸)
- 蓝底证件照怎么制作 证件照换底色 换尺寸快速搞定(蓝底证件照怎么制作)
- 你喜欢足球吗 足球如何点亮世界的(足球如何点亮世界的)
- 不可分鸽是什么梗(不可分鸽是什么梗)
- 古代的鸽子是爱情的象征,并非和平的使者(古代的鸽子是爱情的象征)
热门推荐
- 修改mysql安装服务名称(Apache为mysql以及自己的项目设置虚拟路径)
- SQLServer设置客户端使用IP地址登录的图文详解(SQLServer设置客户端使用IP地址登录的图文详解)
- htop命令使用教程(.htaccess使用方法总结)
- 如何查看iis日志
- pythonmatplotlib绘制立体图形(python3使用matplotlib绘制散点图)
- selenium怎么做集群(基于linux配置selenium环境并实现运行)
- filezilla连接linux服务器连不上(filezilla搭建ftp服务器教程)
- iis搭建网站的地址(IIS站点所有文件直接下载 强制下载)
- 海外云服务器租用要怎么选择?(海外云服务器租用要怎么选择?)
- ExtJs 中Viewport 的使用
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9