类的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()第二个参数传的是类的时候,建议调用父类的类方法和静态方法。
类的super的更多相关文章
- Python类中super()和__init__()的关系
Python类中super()和__init__()的关系 1.单继承时super()和__init__()实现的功能是类似的 class Base(object): def __init__(sel ...
- 【转】python类中super()和__init__()的区别
[转]python类中super()和__init__()的区别 单继承时super()和__init__()实现的功能是类似的 class Base(object): def __init__(se ...
- 【转】python---方法解析顺序MRO(Method Resolution Order)<以及解决类中super方法>
[转]python---方法解析顺序MRO(Method Resolution Order)<以及解决类中super方法> MRO了解: 对于支持继承的编程语言来说,其方法(属性)可能定义 ...
- 类继承-super, 私有变量
多继承 class A: def ces(self): print('a-ces') class B(A): def ces(self): print('b-ces') class C(A): def ...
- python---方法解析顺序MRO(Method Resolution Order)<以及解决类中super方法>
MRO了解: 对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就需要对当前类和基类进行搜索以确定方法所在的位置.而搜索的顺序就是所谓的「方法解析顺序」(M ...
- python类、super函数
#PYTHON语言及其应用学习笔记 1.创建简单的类 class Person(): #python中特殊的对象初始化方法__init__,一个特殊的函数名 #当你在类声明里定义__init__()方 ...
- js--class类、super和estends关键词的学习笔记
前言 JavaScript 语言在ES6中引入了 class 这一个关键字,在学习面试的中,经常会遇到面试官问到谈一下你对 ES6 中class的认识,同时我们的代码中如何去使用这个关键字,使用这个关 ...
- python类中super()和__init__()的区别
class Base(object): def __init__(self): print 'Base create' class childB(Base): def __init__(sel ...
- python类继承的重写和super
给已经存在的类添加新的行为,继承是非常好的实现方式.但是如果要改变行为呢?比如在Python继承扩展内置类,我们的contact类只允许一个名字和一个邮箱,但是如果要对某些人增加电话号码呢?这里可以通 ...
随机推荐
- CSS——你所不知的 CSS ::before 和 ::after 伪元素用法(转)
你所不知的 CSS ::before 和 ::after 伪元素用法 CSS 有两个说不上常用的伪类 :before 和 :after,偶尔会被人用来添加些自定义格式什么的,但是它们的功用不仅于此.前 ...
- Python高级编程之生成器(Generator)与coroutine(一):Generator
转载请注明出处:点我 这是一系列的文章,会从基础开始一步步的介绍Python中的Generator以及coroutine(协程)(主要是介绍coroutine),并且详细的讲述了Python中coro ...
- svn 脚本替换
#!/bin/bashfor i in `find /home/20180629tmp/svnfwq/uadminv4 -name .svn` do echo $i aa=`dirname $i` b ...
- Differential Geometry之第八章常Gauss曲率曲面
第八章.常Gauss曲率曲面 1.常正Gauss曲率曲面 2.常负Gauss曲率曲面与Sine-Gordon方程 3.Hilbert定理 4.Backlund变换 4.1.线汇与焦曲面 4.2.Bac ...
- Spring Cloud 架构
我们从整体来看一下Spring Cloud主要的组件,以及它的访问流程 1.外部或者内部的非Spring Cloud项目都统一通过API网关(Zuul)来访问内部服务. 2.网关接收到请求后,从注册中 ...
- 【BZOJ】1681: [Usaco2005 Mar]Checking an Alibi 不在场的证明(spfa)
http://www.lydsy.com/JudgeOnline/problem.php?id=1681 太裸了.. #include <cstdio> #include <cstr ...
- ACM计算几何模板——圆和球
#include <iostream> #include <cmath> using namespace std; #define eps 1e-10 /********** ...
- HTTP报文-->MVC
引用 学习Web开发不好好学习HTTP报文,将会“打拳不练功,到老一场空”,你花在犯迷糊上的时间比你沉下心来学习HTTP的时间肯定会多很多. HTTP请求报文解剖 HTTP请求报文由3部分组成(请求行 ...
- 搭建 Docker-Registry 私有仓库
官方已经提供了很多版本的 Linux 镜像,直接从官方仓库(Public Repositories)下载就可以了.如果考虑到安全性和速度,我们可能会想在自己局域网里架设一个私有仓库(Private R ...
- mysql备份数据库常见问题
如果遇到mysqldump: unknown option '--no-beep' ,可以加上 --no-dafaults 命令结尾无需分号 mysqldump --no-defaults -uro ...