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

那我现在总结一下:

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

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

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

super()使用方法的更多相关文章

  1. [super init]方法的调用

    当重新覆盖父类的init方法时,需要调用[super init]方法确认父类中的init是返回一个实例,而不是一个空的实例. 那为什么要调用这个呢? 我得猜测是这样的:因为这是一个初始化方法,需要对对 ...

  2. super.getClass()方法调用

    下面程序的输出结果是多少?import java.util.Date;public class Test extends Date{public static void main(String[] a ...

  3. Java Super 覆盖方法

    子类从父类中继承方法,有时候,子类需要修改父类中定义的方法的实现,这称作方法覆盖. 比如,GeometricObject类中的toString方法返回表示集合对象的字符串,这个方法就可以被覆盖,返回表 ...

  4. super.getClass()方法

    下面程序的输出结果是多少? importjava.util.Date; public class Test extends Date{ public static void main(String[] ...

  5. python super超类方法

    super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO).重复调用( ...

  6. 包、继承、Super、方法重写

    1 包_继承 1.1 包 包(package) 用于管理程序中的类,主要用于解决类的同名问题.包可以看出目录. 包的作用 [1] 防止命名冲突. [2] 允许类组成一个单元(模块),便于管理和维护 [ ...

  7. 【Java面试题】14 super.getClass()方法调用

    下面程序的输出结果是多少? import java.util.Date; public class Test extends Date{ public static void main(String[ ...

  8. super.getClass()方法调用?

    下面程序的输出结果是多少? import java.util.Date; public class Test extends Date{ public static void main(String[ ...

  9. super().__init__()方法

    class first(object): def __init__(self,age,name): self.age = age self.name = name class second(first ...

随机推荐

  1. kotlin递归&尾递归优化

    递归: 对于递归最经典的应用当然就是阶乘的计算啦,所以下面用kotlin来用递归实现阶乘的计算: 编译运行: 那如果想看100的阶乘是多少呢? 应该是结果数超出了Int的表述范围,那改成Long型再试 ...

  2. javaWeb中的session和cookie

    Cookie Cookie 是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者 在客户端进行处理的数据,放在本地的计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少 ...

  3. Java 工具类 IpUtil - 获取本机所有 IP 地址,LocalHost 对应地址 IP

    Java 工具类 IpUtil - 获取本机所有 IP 地址,LocalHost 对应地址 IP IP 工具类 源代码: /** * <p> * * @author XiaoPengwei ...

  4. 制作自定义系统iso镜像

    一.制作自己的ISO启动盘篇 在需要安装特定系统的时候,我们使用原版的linux系统盘镜像来安装,需要手动操作N多步,在机器非常多的环境下,这种方式显然不理想,这是我我们就需要制作我们特定的系统盘来简 ...

  5. Hibernate的CRUD配置及简单使用

    参考博客:https://blog.csdn.net/qq_38977097/article/details/81326503 1.首先是jar包,可以在官网下载. 或者点击下面链接下载 链接:htt ...

  6. charindex函数--->检索字符在字符串中的起始位置

    语法:CHARINDEX(检索字符,被检索字符串) 返回值:检索字符在被检索字符串中的起始位置.

  7. Prism框架实战——订餐软件

    参考B站刘铁猛老师的订餐软件https://www.bilibili.com/video/av29782724?from=search&seid=6787438911056306128 环境: ...

  8. 【Python网络】网络协议篇

    OSI七层协议 互联网协议按照功能不同分为OSI七层 或 TCP/IP五层 或 TCP/IP四层 每层运行常见物理设备 TCP/IP五层模型讲解 每层都运行特定的协议,越往上越靠近用户,越往下越靠近硬 ...

  9. 交换机配置——跨交换机划分VLAN配置

    一.实验要求:实现跨交换地划分vlan的配置任务,使同一vlan下的主机能相互通讯 二.拓扑图如下; 三.具体实验步骤: S1交换机配置: S1>enable  --进入特权模式S1#confi ...

  10. Codevs 1213 解的个数(exgcd)

    1213 解的个数 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 已知整数x,y满足如下面的条件: ax+by+c=0 p< ...