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 约定¶ 单继承¶ 多 ...
随机推荐
- 组件与.NET互操作
组件 1.何谓组件技术? 组件技术就是利用某种编程手段,将一些人们所关心的,但又不便于让最终用户去直接操作的细节进行了封装,同时对各种业务逻辑规则进行了实现,用于处理用户的内部操作细节,甚至于将安全机 ...
- 初识用.NET Remoting来开发分布式应用
一..NET Remoting简介: .NET Remoting从某种意义上讲是DCOM的替代品.ASP.NET Web服务十分有用,但是这项技术在企业内联网的解决方案中,对于某些业务请求来说并不快, ...
- bzoj 2763 [JLOI2011]飞行路线——分层图
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2763 分层图两种方法的练习. 1.把图分成k+1层,本层去上面一层的边免费.但空间时间都不算 ...
- java md5 函数
private static final String md5(final String s) { final String MD5 = "MD5"; try { // Creat ...
- delphi 2010安装unidac
UniDAC是一个功能强大的非可视化跨数据库的数据访问组件,可用于Delphi,Delphi for .NET,C++Builder,and Lazarus (Free Pascal).它提供了对流行 ...
- 73个word使用终极技巧
1.问:Word里边怎样设置每页不同的页眉?如何使不同的章节显示的页眉不同? 答:分节,每节可以设置不同的页眉.文件——页面设置——版式——页眉和页脚——首页不同 2.问:请问Word中怎样让每一章用 ...
- Ubuntu14.04安装Matlab2014a
尝试在Ubuntu14.04下安装matlab2014a已经有两天了,即便是照着csdn上的步骤进行安装依然出现了不少问题,究其原因是对linux下的命令不理解,下面就自己摸索结合博客内容总结一下安装 ...
- Elasticsearch5.6安装
Elasticsearch5.6安装 1.下载 5.6.8 sudo curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/e ...
- redirect uri 参数错误 怎么办
这种情况,多数是因为请求地址不合法所致. 去公众号中添加合法的地址. 这种地址需要满足一些条件. 设置地址 满足的条件 保证可以访问到安全文件 如果访问不到的话,将无法保存 这里是文件存放位置 经过这 ...
- 怎么样使用yum来安装mysql
linux下使用yum安装mysql,以及启动.登录和远程访问. 1.安装 查看有没有安装过: yum list installed mysql* rpm -qa | grep mysql* 查看有没 ...