你会使用super()吗?你确定你了解它吗?
我们经常在类的继承当中使用super(), 来调用父类中的方法。例如下面:
- 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的类:
- class B(A):
- def func(self):
- A.func(self)
- print('LuffyCity')
这样能实现相同的效果,只不过传了一个self参数。那为什么还要使用super()呢?
那我看看有这样的一个继承关系的类(钻石继承):
- Base
- / \
- / \
- A B
- \ /
- \ /
- C
代码是这样的:
- 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()的写法:
- 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列表,用来管理类的继承顺序。
- 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至少需要一个参数,并且类型需要是类。
不传参数的会报错。只传一个参数的话是一个不绑定的对象,不绑定的话也就没什么用了。
- print(super(C))
- print(super())
输出结果:
- RuntimeError: super(): no arguments
- <super: <class 'C'>, NULL>
在定义类当中可以不写参数,Python会自动根据情况将两个参数传递给super。
- 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的实例,可以不是直接的实例,是子类的实例也行。
- 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的子类。
- 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的参数填写上,并且实例化,看看输出的结果。
- 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:
- 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)的区别。
代码如下:
- 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。那他们的方法是否是一样的呢?测试一下。
- print(super(C, C).func is super(C, c_obj).func)
- print(super(C, C).func == super(C, c_obj).func)
输出结果:
- False
- False
他俩的方法既不是指向同一个,值还不相等。是不是搞错了呢?再试试下面的看看。
- 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 的确是不同的。那打印出来看看有什么区别:
- 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的第二个参数传递的是对象,得到的是绑定方法。
函数和绑定方法的区别就不再赘述了,在这里想得到一样的结果,只需要给函数传递一个参数,而绑定方法则不需要传递额外的参数了。
- 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()吗?你确定你了解它吗?的更多相关文章
- 子类继承父类时JVM报出Error:Implicit super constructor People() is undefined for default constructor. Must define an explicit constructor
当子类继承父类的时候,若父类没有定义带参的构造方法,则子类可以继承父类的默认构造方法 当父类中定义了带参的构造方法,子类必须显式的调用父类的构造方法 若此时,子类还想调用父类的默认构造方法,必须在父类 ...
- [LeetCode] Super Ugly Number 超级丑陋数
Write a program to find the nth super ugly number. Super ugly numbers are positive numbers whose all ...
- Maven Super POM
Maven super POM defines some properties. Three ways to find it ${M2_HOME}/lib/maven-model-builder-3. ...
- java基础 super 子类调用父类
如果希望在子类中,去调用父类的构造方法,要求在子类的构造函数调用 example如下: package test; /* * 如果希望在子类中,去调用父类的构造方法,要求在子类的构造函数调用 * */ ...
- Python类中super()和__init__()的关系
Python类中super()和__init__()的关系 1.单继承时super()和__init__()实现的功能是类似的 class Base(object): def __init__(sel ...
- java方法重载(overload)、重写(override);this、super关键简介
一.方法重载: 条件:必须在一个类中,方法名称相同,参数列表不同(包括:数据类型.顺序.个数),典型案例构 造方重载. 注意:与返回值无关 二.方法重写: 条件: (1)继承某个类或实现某接口 (2 ...
- Java super关键字活用
在实际开发中我们要自定义组件,就需要继承自某个组件类,如果我们自定义的这个组件类也需要像被继承的这个组件类一样,拥有丰富的构造方法. 关键字super的作用就更加显得尤为重要了,你可以在堆砌自己自定义 ...
- 深入super,看Python如何解决钻石继承难题 【转】
原文地址 http://www.cnblogs.com/testview/p/4651198.html 1. Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通 ...
- 关于[super dealloc]
销毁一个对象时,需要重写系统的dealloc方法来释放当前类所拥有的对象,在dealloc方法中需要先释放当前类中所有的对象,然后再调用[super dealloc]释放父类中所拥有的对象.如先调用[ ...
- ubuntu super daemon设置
super daemon是一个在Linux下面全面管理自己服务设置的东东,他可以接管很多服务的设定,只需要在/etc/xinetd.d/下面放置好自己的配置文件就可以了,那么,具体应该怎么配置呢? ...
随机推荐
- PAT 甲级 1029 Median
https://pintia.cn/problem-sets/994805342720868352/problems/994805466364755968 Given an increasing se ...
- 提升jmeter自身性能
JMeter负载测试时使用GUI界面和较多的收集测试结果的监听器容易造成jmeter的性能瓶颈,远程测试时的控制台尤为明显.提升JMeter负载测试时性能的方法如下: 官方的解决办法:http://j ...
- 清华集训2015-Day 1
玛里苟斯 一个大小为 \(n\) 的可重集合 \(a\) ,求 \(\mathbb E[x^k]\) ,其中 \(x\) 为 \(a\) 的一个子集的异或和. \(n\le 10^5,1\le k\l ...
- BZOJ2001 [Hnoi2010]City 城市建设 CDQ分治
2001: [Hnoi2010]City 城市建设 Time Limit: 20 Sec Memory Limit: 162 MB Description PS国是一个拥有诸多城市的大国,国王Lou ...
- CF438D The Child and Sequence(线段树)
题目链接:CF原网 洛谷 题目大意:维护一个长度为 $n$ 的正整数序列 $a$,支持单点修改,区间取模,区间求和.共 $m$ 个操作. $1\le n,m\le 10^5$.其它数均为非负整数且 ...
- 【uoj34】 多项式乘法
http://uoj.ac/problem/34 (题目链接) 题意 求两个多项式的乘积 Solution 挂个FFT板子. 细节 FFT因为要满足$n$是$2$的幂,所以注意数组大小. 代码 // ...
- Codeforces Round #541
因为这次难得不在十点半(或是更晚),大家都在打,然后我又双叒叕垫底了=.= 自己对时间的分配,做题的方法和心态还是太蒻了,写的时候经常写一半推倒重来.还有也许不是自己写不出来,而是在开始写之前就觉得自 ...
- ASP.NET MVC —— Model之一模型模板
http://www.cnblogs.com/lzhp/archive/2013/03/25/2981650.html Mvc model系列文章主要分为三部分:Model Templates,Mod ...
- python urllib和urllib3包使用(转载于)
urllib.request 1. 快速请求 2.模拟PC浏览器和手机浏览器 3.Cookie的使用 4.设置代理 urllib.error URLError HTTPError urllib.par ...
- Vue实例的生命周期(钩子函数)
Vue实例的生命钩子总共有10个 先上官方图: 下面时一个vue实例定义钩子函数的例子: var app=new Vue({ el:'#app', beforeCreate:function(){ c ...