Python super( )函数用法总结
作者:小皇鱼 发布时间:2022-07-09 23:23:10
一、super( ) 的用途
了解 super() 函数之前,我们首先要知道 super() 的用途是啥?
主要用来在子类中调用父类的方法。
多用于多继承问题中,解决查找顺序(MRO)、重复调用(钻石继承)等种种问题。
二、了解 super 的基础信息
语法格式:
super([type[, object-or-type]])
函数描述:
返回一个代理对象,它会将方法调用委托给 type 的父类或兄弟类。
参数说明:
type —— 类,可选参数。object-or-type —— 对象或类,一般是 self,可选参数。
返回值:
super object —— 代理对象。
help 帮助信息:
>>> help(super)
Help on class super in module builtins:
class super(object)
| 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)
| Typical use to call a cooperative superclass method:
| class C(B):
| def meth(self, arg):
| super().meth(arg)
| This works for class methods too:
| class C(B):
| @classmethod
| def cmeth(cls, arg):
| super().cmeth(arg)
... ...
super 是一个继承自 object 的类,调用 super() 函数其实就是 super 类的实例化。
根据官方文档的解释 super() 函数返回的对象 —— super object,就是一个代理对象。
super() 有四种参数的组合形式。
super()适用于类的静态方法。
三、典型用法
3.1 单继承问题
首先我们看一个最基本的子类调用父类方法的示例:
>>> class A:
def funxx(self):
print("执行 A 中的 funxx 方法 ... ...")
>>> class B(A):
def funxx(self):
A.funxx(self) # 通过类名调用父类中的同名方法,self 参数代表 B 类的实例对象 b
print("执行 B 中的 funxx 方法 ... ...")
>>> b = B()
>>> b.funxx()
执行 A 中的 funxx 方法 ... ...
执行 B 中的 funxx 方法 ... ...
定义一个继承自 A 类的子类 B,并在 B 类中重写 funxx() 方法,B 中的 funxx() 是对 A 中的 funxx() 功能的拓展。
因为是拓展了 A 类的 funxx() 方法的功能,所以其任然保留了原功能,即要在子类 B 中调用父类的同名方法来实现原有功能。
上面的示例中是通过 A 类类名调用 A 类中的同名方法来实现的,而第一个参数 self 实际传递的是 B 类的实例 b。
使用 super() 函数来实现父类方法的调用:
>>> class A:
def funxx(self):
print("执行 A 中的 funxx 方法 ... ...")
>>> class B(A):
def funxx(self):
super().funxx()
print("执行 B 中的 funxx 方法 ... ...")
>>> b = B()
>>> b.funxx()
执行 A 中的 funxx 方法 ... ...
执行 B 中的 funxx 方法 ... ...
通过执行的结果可以看出实现了和普通类名调用的结果是一样的。
在具有单继承的类层级结构中,super 引用父类而不必显式地指定它们的名称,从而令代码更易维护。(官方文档描述)
也就是说,在子类中不再用父类名调用父类方法,而是用一个代理对象调用父类方法,这样当父类名改变或者继承关系发生变化时,不用对每个调用处都进行修改。
3.2 单继承问题拓展
在 help()
的帮助信息中,也说明了类中使用 super()
不带参数的形式等同于 super(__class__, <first argument>)
这种形式。这也是 Python 2.x 和 Python 3.x 关于 super()
的区别。
改写之前的单继承问题的代码:
>>> class A:
def funxx(self):
print("执行 A 中的 funxx 方法 ... ...")
>>> class B(A):
def funxx(self):
super(B, self).funxx()
print("执行 B 中的 funxx 方法 ... ...")
>>> b = B()
>>> b.funxx()
执行 A 中的 funxx 方法 ... ...
执行 B 中的 funxx 方法 ... ...
基本的调用方法
A.funxx(self)
,其中 self 指代实例对象 b。用语言描述为:实例对象 b 通过 A 类名调用方法funxx()
。官方描述:返回一个代理对象,它会将方法调用委托给 type 的父类或兄弟类。用语言描述为:代理对象 super 通过 type 的父类或兄弟类调用其中的方法。
我们发现 super 是通过参数设置来选择调用哪个父类的方法。其中第二个参数给出 MRO(方法解析顺序),也就是搜索目标方法的顺序,第一个参数则给出搜索目标方法的范围。
例如
super(B, self)
,第一个参数为 B,第二个参数 self 为实例 b,其所在类的继承顺序(MRO)为:B→A→object。所以调用时是在 B 的父类 A 中寻找,如找不到目标方法则会在更上一层的 object 中寻找。
示例:
class A:
pass
class B(A):
pass
class C(A):
def funxx(self):
print("找到 funxx() 位于 C 中...")
class D(A):
pass
class E(B, C):
pass
class F(E, D):
def funff(self):
print("执行 F 中的 funff()...")
super(E, self).funxx()
print(f"F 类的 MRO : {F.__mro__}")
f = F()
f.funff()
运行结果:
F 类的 MRO : (<class '__main__.F'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
执行 F 中的 funff()...
找到 funxx() 位于 C 中...
我们可以看出 F 类的 MRO:F→E→B→C→D→A→object。
super()
函数的第一个参数为:E,目标是调用 E 类的父类 B 中的funxx()
方法,可惜 B 类中没找到,在 B 类的兄弟类 C 中找到了,符合要求。
3.3 重复调用问题
重复调用问题 也称 钻石继承问题 或 菱形图问题。
先来看看普通调用方法在:
>>> class A:
def __init__(self):
print("打印属性 a")
>>> class B(A):
def __init__(self):
print("打印属性 b")
A.__init__(self)
>>> class C(A):
def __init__(self):
print("打印属性 c")
A.__init__(self)
>>> class D(B, C):
def __init__(self):
print("打印属性 d")
B.__init__(self)
C.__init__(self)
>>> d = D()
打印属性 d
打印属性 b
打印属性 a
打印属性 c
打印属性 a
因为 B,C 都继承自 A,所以当 D 在实例化时,A 的构造函数被执行了两次。这就是所谓的重复调用问题。
很显然,我们只需要调用一次就可以了,重复的调用只会造成资源浪费。
接下来我们使用 super() 函数来调用:
>>> class A:
def __init__(self):
print("打印属性 a")
>>> class B(A):
def __init__(self):
print("打印属性 b")
super().__init__() # super() 等同于 super(B, self)
>>> class C(A):
def __init__(self):
print("打印属性 c")
super().__init__() # super() 等同于 super(C, self)
>>> class D(B, C):
def __init__(self):
print("打印属性 d")
super(D, self).__init__()
>>> d = D()
打印属性 d
打印属性 b
打印属性 c
打印属性 a
查看输出结果我们发现虽然解决了重复调用问题,但是输出结果的顺序好像与我们想的有所区别。我们的惯性思维是:先执行 D 类的
__init__()
方法,接着调用 B 类的__init__()
方法,B 类的构造方法中又调用了父类 A 的__init_()
方法,然后再是调用 C 类的__init_()
方法,该方法也调用了父类 A 的__init__()
方法。所以执行的结果应该是:打印属性 d,打印属性 b,打印属性 a,打印属性 c。为何结果不是我们想的那样呢,首先我们要知道 D 类中的第二个参数 self 为 D 的实例 d,它提供的 MRO 为:D→B→C→A→object。所以 D 类中的
super()
函数产生的是 d 的代理对象,当其调用父类 B 的__init__()
时,B 的super()
的第二个参数为 D 中的 super object,其所提供的 MRO 依旧为:D→B→C→A→object。也就是说 B 中的super()
调用的是它的上一级 C 中的__init__()
,而不是 A 中的__init__()
。所以执行的结果是:打印属性 d,打印属性 b,打印属性 c,打印属性 a。
3.4 super(type) 问题
>>> class A:
def funxx(self):
print("...A...")
>>> class B(A):
def funxx(self):
print("...B...")
>>> sa = super(B)
>>> print(sa)
<super: <class 'B'>, NULL>
>>> print(type(sa))
<class 'super'>
可以看出 super(type) 返回的是一个无效的对象,或者是未绑定的 super object。
来源:https://blog.csdn.net/qq_41961087/article/details/117674563


猜你喜欢
- 去除HTML代码中所有标签<% '****************************** '函数:RemoveH
- 前言对于我这种小白来说,本地环境搭建常规的操作一向是直接去go官网下载go安装包,本机进行安装,然后配置相应的GOROOT和GOPATH,再
- tomorrow是我最近在用的一个爬虫利器,该模块属于第三方的一个模块,使用起来非常的方便,只需要用其中的threads方法作为装饰器去修饰
- 描述在linux中获取进程cmdline时遇到隐藏符号问题,如下:[root@vm010066016161 /root]#cat /proc
- 前言:大家都知道,只要进行数据交互,肯定就要去请求接口,数据请求的方式有vue-resource axios fetch
- 大家好,我是才哥。最近周末也加班了,害…有刚接触python的粉丝同学在运行此前《》的完整代码遇到以下问题,然后…好吧,今天我们就专门介绍一
- 本文实例为大家分享了pygame实现移动底座弹球的具体代码,供大家参考,具体内容如下输出结果:实现代码:# -*- coding: utf-
- 用到给视频添加背景音乐,并改变音量。记录一下,与碰到同样问题的朋友共享。import subprocessinmp4='E:/Pyc
- Python Logging原来真的远比我想象的要复杂很多很多,学习路线堪比git。但是又绕不过去,alternatives又少,所以必须要
- 解决方法:应对这种情况有以下几种方法:1、购买第三方软件(一般估计很少人买)2、自己编程一个小软件来执行,但是这个逻辑性要求比较高,而且编程
- 编写Python SDK代码工程目录结构├──── easyhttp
- ip正则式为:r'(([12][0-9][0-9]|[1-9][0-9]|[1-9])\.){3,3}([12][0-9][0-9]
- 使用SQL Server事件探查器工具,你可用一个捕获到的跟踪来收集有关服务器的重要信息。与索引优化向导(Index Tuning Wiza
- 正确的安装1、先安装packagingpython3 -m pip install packaging执行这个命令后会提示这样安装成功Def
- 在做JS开发时,我们将第三方复杂的插件进行封装,然后对外公开一个很简单的方法接口,这是开发时常用的方法,在JS里,我们的方法参数通常使用JQ
- 一、DSE算法背景介绍1. DES的采用1979年,美国银行协会批准使用1980年,美国国家标准局(ANSI)赞同DES作为私人使用的标准,
- 1.查看mysql上都有哪些库mysql> show databases \G***************************
- ORACLE的数据字典是数据库的重要组成部分之一,它随着数据库的产生而产生, 随着数据库的变化而变化, 体现为sys用户下的一些表和视图。数
- 我就废话不多说了,大家还是直接看代码吧~def list_dict(list_data): dict_data = {} &nb
- 打过了趟深圳回来后,已经快半个月,在广州购书中心逛了下,发现2本前端书《重构HTML-改善WEB应用的设计》、《CSS3 实战》,看了一半《