Python ---- super()使用
Python ---- super()
我们经常在类的继承当中使用super(), 来调用父类中的方法。例如下面:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class A: def func( self ): print ( 'OldBoy' ) class B(A): def func( self ): super ().func() print ( 'LuffyCity' ) A().func() B().func() |
输出的结果为:
OldBoy
OldBoy
LuffyCity
A实例化的对象调用了func方法,打印输出了 Oldboy;
B实例化的对象调用了自己的func方法,先调用了父类的方法打印输出了 OldBoy ,再打印输出 LuffyCity 。
这样是Python3的写法,今天咱们也只讨论Python3中的super。
如果不使用super的话,想得到相同的输出截个,还可以这样写B的类:
1
2
3
4
|
class B(A): def func( self ): A.func( self ) print ( 'LuffyCity' ) |
这样能实现相同的效果,只不过传了一个self参数。那为什么还要使用super()呢?
那我看看有这样的一个继承关系的类(钻石继承):
1
2
3
4
5
6
7
|
Base / \ / \ A B \ / \ / C |
代码是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Base: def __init__( self ): print ( 'Base.__init__' ) class A(Base): def __init__( self ): Base.__init__( self ) print ( 'A.__init__' ) class B(Base): def __init__( self ): Base.__init__( self ) print ( 'B.__init__' ) class C(A, B): def __init__( self ): A.__init__( self ) B.__init__( self ) print ( 'C.__init__' ) C() |
输出的结果是:
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__
每个子类都调用父类的__init__方法,想把所有的初始化操作都做一遍,但是出现了一个问题,Base类的__init__方法被调用了两次,这是多余的操作,也是不合理的。
那我们改写成使用super()的写法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class Base: def __init__( self ): print ( 'Base.__init__' ) class A(Base): def __init__( self ): super ().__init__() print ( 'A.__init__' ) class B(Base): def __init__( self ): super ().__init__() print ( 'B.__init__' ) class C(A, B): def __init__( self ): super ().__init__() print ( 'C.__init__' ) C() |
输出的结果是:
Base.__init__
B.__init__
A.__init__
C.__init__
这样执行的结果就比较满意,是大多数人想要的结果。那为什么会是这样的结果呢?
那是因为我们每定义一个类的时候,Python都会创建一个MRO列表,用来管理类的继承顺序。
1
2
|
print (C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>] |
Python通过这个列表从左到右,查找继承的信息。Python3中的类都是新式类,都有这个mro属性,能看出来是广度优先的查找原则。经典类就没有mro属性,但它的查找原则是深度优先。
那我回到super的问题上来,让我们先看看super的官方定义。
super([type[, object-or-type]])
返回一个代理对象,该对象将方法调用委托给类的父类或兄弟类。这对于访问类中已重写的继承方法非常有用。搜索顺序与getattr()使用的搜索顺序相同,只是类型本身被跳过。
类的__mro__属性列出了getattr()和super()使用的方法解析搜索顺序。属性是动态的,可以在继承层次结构更新时进行更改。
看到官方的解释就可以很清楚的明白,super是一个类,实例化之后得到的是一个代理的对象,而不是得到了父类,并且我们使用这个代理对象来调用父类或者兄弟类的方法。
那我们再看看super的使用方法:
super() -> same as super(__class__, <first argument>)
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)
super至少需要一个参数,并且类型需要是类。
不传参数的会报错。只传一个参数的话是一个不绑定的对象,不绑定的话也就没什么用了。
1
2
|
print ( super (C)) print ( super ()) |
输出结果:
RuntimeError: super(): no arguments
<super: <class 'C'>, NULL>
在定义类当中可以不写参数,Python会自动根据情况将两个参数传递给super。
1
2
3
4
5
6
7
8
|
class C(A, B): def __init__( self ): print ( super ()) super ().__init__() print ( 'C.__init__' ) C() |
输出结果:
<super: <class 'C'>, <C object>>
Base.__init__
B.__init__
A.__init__
C.__init__
所以我们在类中使用super的时候参数是可以省略的。
第三种用法, super(type, obj) 传递一个类和对象,得到的是一个绑定的super对象。这还需要obj是type的实例,可以不是直接的实例,是子类的实例也行。
1
2
3
|
a = A() print ( super (A, a)) print ( super (Base, a)) |
输出结果:
Base.__init__
A.__init__
<super: <class 'A'>, <A object>>
<super: <class 'Base'>, <A object>>
第三种用法, super(type, type2)传递两个类,得到的也是一个绑定的super对象。这需要type2是type的子类。
1
2
3
|
print ( super (Base, A)) print ( super (Base, B)) print ( super (Base, C)) |
输出结果:
<super: <class 'Base'>, <A object>>
<super: <class 'Base'>, <B object>>
<super: <class 'Base'>, <C object>>
接下来我们就该说说查找顺序了,两个参数,是按照那个参数去计算MRO呢?
我们将C类中的super的参数填写上,并且实例化,看看输出的结果。
1
2
3
4
|
class C(A, B): def __init__( self ): super (C, self ).__init__() print ( 'C.__init__' ) |
输出结果:
Base.__init__
B.__init__
A.__init__
C.__init__
看结果和之前super没填参数的结果是一样的。
那我们将super的第一个参数改为A:
1
2
3
4
|
class C(A, B): def __init__( self ): super (A, self ).__init__() print ( 'C.__init__' ) |
输出结果:
Base.__init__
B.__init__
C.__init__
咦!?那A.__init__怎么跑丢了呢?多出来了B.__init__呢?
这是应为Python是按照第二个参数来计算MRO,这次的参数是self,也就是C的MRO。在这个顺序中跳过一个参数(A)找后面一个类(B),执行他的方法。
知道这个后,输出的结果就可以理解了。 super(A, self).__init__() 没有执行Base的方法,而是执行了B的方法。
那我们接下来说说 super(type, obj) 和 super(type, type2)的区别。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class Base: def func( self ): return 'from Base' class A(Base): def func( self ): return 'from A' class B(Base): def func( self ): return 'from B' class C(A, B): def func( self ): return 'from C' c_obj = C() print ( super (C, C)) print ( super (C, c_obj)) |
输出结果:
<super: <class 'C'>, <C object>>
<super: <class 'C'>, <C object>>
两次的打印结果一模一样,verygood。那他们的方法是否是一样的呢?测试一下。
1
2
|
print ( super (C, C).func is super (C, c_obj).func) print ( super (C, C).func = = super (C, c_obj).func) |
输出结果:
False
False
他俩的方法既不是指向同一个,值还不相等。是不是搞错了呢?再试试下面的看看。
1
2
3
4
5
6
|
c1 = super (C, C) c2 = super (C, C) print (c1 is c2) print (c1 = = c2) print (c1.func is c2.func) print (c1.func = = c2.func) |
输出结果:
False
False
True
True
c1和c2不是一个对象,但是他们的方法却是相同的。
那 super(C, C).func 和 super(C, c_obj).func 的确是不同的。那打印出来看看有什么区别:
1
2
|
print ( super (C, C).func) print ( super (C, c_obj).func) |
输出结果:
<function A.func at 0x0000000009F4D6A8>
<bound method A.func of <__main__.C object at 0x00000000022A94E0>>
super的第二个参数传递的是类,得到的是函数。
super的第二个参数传递的是对象,得到的是绑定方法。
函数和绑定方法的区别就不再赘述了,在这里想得到一样的结果,只需要给函数传递一个参数,而绑定方法则不需要传递额外的参数了。
1
2
|
print ( super (C, C).func(c_obj)) print ( super (C, c_obj).func()) |
输出结果:
from A
from A
那我现在总结一下:
- super()使用的时候需要传递两个参数,在类中可以省略不写,我们使用super()来找父类或者兄弟类的方法;
- super()是根据第二个参数来计算MRO,根据顺序查找第一个参数类后的方法。
- super()第二个参数是类,得到的方法是函数,使用时要传self参数。第二个参数是对象,得到的是绑定方法,不需要再传self参数。
给使用super()的一些建议:
- super()调用的方法要存在;
- 传递参数的时候,尽量使用*args 与**kwargs;
- 父类中的一些特性,比如【】、重写了__getattr__,super对象是不能使用的。
- super()第二个参数传的是类的时候,建议调用父类的类方法和静态方法。
Python ---- super()使用的更多相关文章
- Python super继承详解
MRO(Method resolution order)是python用来解析方法调用顺序的,mro中记录了一个类的所有基类的类类型序列,super不是简单地调用基类的方法,而是按照MRO中的顺序来调 ...
- python super
http://hi.baidu.com/thinkinginlamp/item/3095e2f52c642516ce9f32d5 Python中对象方法的定义很怪异,第一个参数一般都命名为self(相 ...
- Python super使用
一 基础使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现,比如: #!/usr ...
- python super参数错误
# -*- coding:utf-8 _*-"""@author:Administrator@file: yamlparser.py@time: 2018/09/07&q ...
- python super超类方法
super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO).重复调用( ...
- Python super() 函数的概念和例子
概念: super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO).重 ...
- python: super原理
super() 的入门使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现,比如: ...
- python super()函数
super()函数是用来调用父类(超类)的一个方法 super()的语法: python 2 的用法: super(Class, self).xxx # class是子类的名称 class A(ob ...
- (转)Python: super 没那么简单
原文:https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html python 约定¶ 单继承¶ 多 ...
随机推荐
- 在oracle里,如何取得本周、本月、本季度、本年度的第一天和最后一天的时间
在oracle里,如何取得本周.本月.本季度.本年度的第一天和最后一天的时间 --本周 select trunc(sysdate,'d')+1 from dual; select trunc(sysd ...
- 前阿里DT总监欧吉良猝死:一代大神勾践陨落滴滴
欧吉良 阿里巴巴集团数据技术及产品部(DT)总监,淘宝网&天猫BI团队负责人,集团数据委员会数据运营组组长,阿里数据大学校长:2007年7月正式加入阿里,先后在支付宝.天猫.淘宝.数据技术及产 ...
- (转)【Android】获取Mac地址【2】
[Android]获取Mac地址[2] 之前写了[Android]获取Mac地址[1]有些不够详细,现在贴上一些其他代码,仅供参考. (1) 调用android 的API: NetworkInterf ...
- merge into报错ORA-00926、ORA-38014
今天用ibatis写个插入操作,为了兼容修改想使用 merge into语句,以便重复插入时直接 update,具体语句如下: <insert id="wlf"> ME ...
- WM_COMMAND 和 WM_NOTIFY 的区别
当我们按下一个菜单选项,或者一个控件需要通知父窗口一个事件发生(如鼠标单击.双击等),或者快捷键被按下时,Windows将会发送一个 WM_COMMAND 消息给父窗口.那么 WM_COMMAND 消 ...
- (转)使用PowerDesigner生成HTML功能
本文转载自:http://www.cnblogs.com/CowboyProgrammer/archive/2009/04/28/1445423.html 使用PowerDesigner设计数据库关系 ...
- 【转】Jmeter安装成功后的目录介绍
1.bin目录 Jmeter.bat 打开Jmeter主界面 Jmeter使用的日志文件名称被定义到Jmeter.properties中,默认在Jmeter.log可查看日志 2.dosc和prin ...
- java代码反转toCharAT()的用法
总结:反转注意for循环里面的变化 package clientFrame; //字符串反转 public class we { public static void main(String[] ar ...
- PHP中静态变量的使用
1.定义静态变量 public static $endpoint,$accessKeyId,$accessKeySecret,$bucket; 2.静态变量赋值 protected function ...
- python学习(十五) 屏幕抓取
15.1 屏幕抓取 15.1.1 Tidy和XHTML解析 Tidy:用来修复不规范且随意的HTML文档的工具. 为什么用XHTML: 和旧版本的HTML之间最主要的区别:HTML可能只用一个开始标签 ...