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

标签:Python,super,函数
0
投稿

猜你喜欢

  • 详解pandas库pd.read_excel操作读取excel文件参数整理与实例

    2021-08-07 00:06:22
  • pytorch中的自定义数据处理详解

    2023-08-21 09:31:35
  • python 循环遍历字典元素的简单方法

    2023-12-15 08:06:15
  • 使用numpy.mean() 计算矩阵均值方式

    2021-12-17 03:55:53
  • Python OpenCV实现图片上输出中文

    2021-07-01 15:00:11
  • Python实现TCP通信的示例代码

    2022-10-03 15:49:17
  • python管理包路径之pycharm自动解决包路径注册

    2023-07-14 14:25:38
  • python根据文件大小打log日志

    2022-04-28 06:29:35
  • Python实现将SQLite中的数据直接输出为CVS的方法示例

    2022-10-01 00:05:32
  • python实现的批量分析xml标签中各个类别个数功能示例

    2021-01-16 00:22:00
  • MySQL索引类型Normal、Unique和Full Text的讲解

    2024-01-20 09:56:19
  • SQL语句优化提高数据库性能

    2024-01-29 03:40:08
  • vue项目中全局引入1个.scss文件的问题解决

    2024-05-29 22:44:38
  • javascript实现锁定网页、密码解锁效果(类似系统屏幕保护效果)

    2023-08-18 20:01:36
  • 详解Go strconv包

    2024-04-23 09:42:08
  • 如何使用moment.js获取本周、前n周、后n周开始结束日期及动态计算周数

    2024-05-28 15:40:15
  • 跟老齐学Python之坑爹的字符编码

    2021-07-13 06:07:38
  • PHP编程实现多维数组按照某个键值排序的方法小结【2种方法】

    2023-09-11 08:28:26
  • 分享8 个常用pandas的 index设置

    2023-10-24 14:22:13
  • Python实现线程状态监测简单示例

    2023-12-07 20:31:25
  • asp之家 网络编程 m.aspxhome.com