我们经常在类的继承当中使用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

那我现在总结一下:

  1. super()使用的时候需要传递两个参数,在类中可以省略不写,我们使用super()来找父类或者兄弟类的方法;
  2. super()是根据第二个参数来计算MRO,根据顺序查找第一个参数类后的方法。
  3. super()第二个参数是类,得到的方法是函数,使用时要传self参数。第二个参数是对象,得到的是绑定方法,不需要再传self参数。

给使用super()的一些建议:

  1. super()调用的方法要存在;
  2. 传递参数的时候,尽量使用*args 与**kwargs;
  3. 父类中的一些特性,比如【】、重写了__getattr__,super对象是不能使用的。
  4. super()第二个参数传的是类的时候,建议调用父类的类方法和静态方法。

你会使用super()吗?你确定你了解它吗?的更多相关文章

  1. 子类继承父类时JVM报出Error:Implicit super constructor People() is undefined for default constructor. Must define an explicit constructor

    当子类继承父类的时候,若父类没有定义带参的构造方法,则子类可以继承父类的默认构造方法 当父类中定义了带参的构造方法,子类必须显式的调用父类的构造方法 若此时,子类还想调用父类的默认构造方法,必须在父类 ...

  2. [LeetCode] Super Ugly Number 超级丑陋数

    Write a program to find the nth super ugly number. Super ugly numbers are positive numbers whose all ...

  3. Maven Super POM

    Maven super POM defines some properties. Three ways to find it ${M2_HOME}/lib/maven-model-builder-3. ...

  4. java基础 super 子类调用父类

    如果希望在子类中,去调用父类的构造方法,要求在子类的构造函数调用 example如下: package test; /* * 如果希望在子类中,去调用父类的构造方法,要求在子类的构造函数调用 * */ ...

  5. Python类中super()和__init__()的关系

    Python类中super()和__init__()的关系 1.单继承时super()和__init__()实现的功能是类似的 class Base(object): def __init__(sel ...

  6. java方法重载(overload)、重写(override);this、super关键简介

    一.方法重载: 条件:必须在一个类中,方法名称相同,参数列表不同(包括:数据类型.顺序.个数),典型案例构 造方重载.  注意:与返回值无关 二.方法重写: 条件: (1)继承某个类或实现某接口 (2 ...

  7. Java super关键字活用

    在实际开发中我们要自定义组件,就需要继承自某个组件类,如果我们自定义的这个组件类也需要像被继承的这个组件类一样,拥有丰富的构造方法. 关键字super的作用就更加显得尤为重要了,你可以在堆砌自己自定义 ...

  8. 深入super,看Python如何解决钻石继承难题 【转】

    原文地址 http://www.cnblogs.com/testview/p/4651198.html 1.   Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通 ...

  9. 关于[super dealloc]

    销毁一个对象时,需要重写系统的dealloc方法来释放当前类所拥有的对象,在dealloc方法中需要先释放当前类中所有的对象,然后再调用[super dealloc]释放父类中所拥有的对象.如先调用[ ...

  10. ubuntu super daemon设置

    super daemon是一个在Linux下面全面管理自己服务设置的东东,他可以接管很多服务的设定,只需要在/etc/xinetd.d/下面放置好自己的配置文件就可以了,那么,具体应该怎么配置呢?   ...

随机推荐

  1. 80C51存储器与C51内存优化

    80C51在物理结构上有四个存储空间:片内程序存储器.片外程序存储器.片内数据存储器和片外数据存储器.但在逻辑上,即从用户使用的角度上,80C51有三个存储空间:片内外统一编址的64KB的程序存储器地 ...

  2. String js删除字符串的最后一个字符三种方法

    字符串 var basic = "abc,def,ghi,"; 第一种 basic = basic.substr(0, basic.length - 1); 第二种 basic = ...

  3. 自学Zabbix3.10.1.5-事件通知Notifications upon events-媒介类型Script

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 自学Zabbix3.10.1.5-事件通知Notifications upon events-媒介 ...

  4. BZOJ 3876 支线剧情 | 有下界费用流

    BZOJ 3876 支线剧情 | 有下界费用流 题意 这题题面搞得我看了半天没看懂--是这样的,原题中的"剧情"指的是边,"剧情点"指的才是点. 题面翻译过来大 ...

  5. 【转】keil5 missing close quote 错误解决

    在使用utf8保存文件的情况下,代码中出现中文字符串会提示missing close quote解决:Options for Target --> C/C++ --> Misc Contr ...

  6. 洛谷4859 BZOJ3622 已经没什么好害怕的了(DP,二项式反演)

    题目链接: 洛谷 BZOJ 题目大意:有两个长为 $n$ 的序列 $a,b$,问有多少种重排 $b$ 的方式,使得满足 $a_i>b_i$ 的 $i$ 的个数比满足 $a_i<b_i$ 的 ...

  7. Atcoder Grand 011 C - Squared Graph

    题意: 给出一个n个点的图,现在构造一个有n^2个点的新图,新图每个点表示为(a,b)(a,b<=n),两个点$(a,b),(c,d)$之间有边当且仅当原图中ac之间有边,bd之间有边. 问新图 ...

  8. NIO[读]、[写]在同一线程(单线程)中执行,让CPU使用率最大化,提高处理效率

    前几天写过一篇文章,讨论重写服务后,用ab进行压力测试,发现使用NIO后没提高什么性能,只是CPU使用率提高了,内存占用降低了. 之前的NIO实现模式,主要参考(基于事件的NIO多线程服务器)http ...

  9. Asp.net MVC3 CSS 模板

    http://www.cnblogs.com/huyq2002/archive/2011/12/16/2289795.html Asp.net MVC3 CSS 模板 如果你现在正在用asp.net ...

  10. lrzsz 移植到 ARM-linux 嵌入式板子上

    特别说明:SSH 或 串口 都可以使用 lrzsz 进行通信 lrzsz是一个Unix通信包,提供XMODEM.YMODEM和ZMODEM文件传输协议.lrzsz以前是Omen科技的主打软件,现在已经 ...