python中super()函数的理解与基本使用


Posted in Python onAugust 30, 2021

前言

Python是一门面向对象的语言,定义类时经常要用到继承,在类的继承中,子类继承父类中已经封装好的方法,不需要再次编写,如果子类如果重新定义了父类的某一方法,那么该方法就会覆盖父类的同名方法,但是有时我们希望子类保持父类方法的基础上进行扩展,而不是直接覆盖,就需要先调用父类的方法,然后再进行功能的扩展,这时就可以通过super来实现对父类方法的调用。

super的用法

看下面一个例子:

class A:
    def func(self):
        print("A的func执行")


class B(A):

    def func(self):
        super().func()
        print("B扩展的func执行")


b = B()
b.func()
# 输出结果为:
# A的func执行
# B扩展的func执行

上面程序中,A是父类,B是A的子类,我们在A类中重定义了func()方法,在B类中重新定义了func()方法,在方法中通过super().func()又调用了父类的方法,所以执行结果才会有A类func()方法输出。

如果经常看Python内置库及第三方库源码的话,你会发现,super用的非常多的地方是在子类中调用父类的初始化__init__()方法,这种用法非常常见。

class A:
    def __init__(self, x):
        self.x = x

class B(A):

    def __init__(self, x, y):
        super().__init__(x)
        self.y = y
    

b = B(1, 2)
print(b.x, b.y)

看到这,你会想到super就是用来获取父类并用来调用父类方法的,这样说对不对呢,其实是不对的,使用supper获取的不是父类,而是MRO列表中的下一个类,所谓MRO列表即方法解析顺序(Method Resolution Order)列表,它代表着类继承的顺序,我们可以使用以下几种获得某个类的MRO列表:

C.mro()
C.__mro__
c.__class__.__mro__

MRO列表的顺序确定经历了很多次的变迁,最新的是通过C3线性化算法来实现的,感兴趣的话可以自行了解一下,总的来说,一个类的MRO列表就是合并所有父类的MRO列表,并遵循以下三条原则:

  • 子类永远在父类前面
  • 如果有多个父类,会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父类

下面来看一下下面这个例子:

class A(Base):
    def func(self):
        print("A的func执行")
        super().func()
        print("A的func执行完毕")


class B(Base):
    def func(self):
        print("B的func执行")
        super().func()
        print("B的func执行完毕")

class C(A, B):
    def func(self):
        print("C的func执行")
        super().func()
        print("C的func执行完毕")


c = C()
c.func()
# 获取MRO列表
print(c.__class__.__mro__)

执行结果如下:

python中super()函数的理解与基本使用

上述程序中,Base是父类,A、B都继承自Base,C继承自 A、B,它们的继承关系就是一个典型的菱形继承,如下:

通过结果我们可以看出,super并不是获取父类并用来调用父类的方法,而是根据MRO列表一次调用下一个类,使用c.__class__.__mro__可以获取MRO列表,MRO列表的顺序是C、A、B、Base、object。

super的原理

super计算方法解析顺序中的下一个类,可以接收两个参数:

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]
  • 通过inst负责生成MRO列表
  • 通过cls定位在MRO列表中的index, 并返回mro[index + 1]

Python super()使用注意事项

Python 中,由于基类不会在 __init__() 中被隐式地调用,需要程序员显式调用它们。这种情况下,当程序中包含多重继承的类层次结构时,使用 super 是非常危险的,往往会在类的初始化过程中出现问题。

混用super与显式类调用

分析如下程序,C 类使用了 __init__() 方法调用它的基类,会造成 B 类被调用了 2 次:

class A:
    def __init__(self):
        print("A",end=" ")
        super().__init__()
class B:
    def __init__(self):
        print("B",end=" ")
        super().__init__()
class C(A,B):
    def __init__(self):
        print("C",end=" ")
        A.__init__(self)
        B.__init__(self)
print("MRO:",[x.__name__ for x in C.__mro__])
C()

运行结果为:

MRO: ['C', 'A', 'B', 'object']
C A B B

出现以上这种情况的原因在于,C 的实例调用 A.__init__(self),使得 super(A,self).__init__() 调用了 B.__init__() 方法。换句话说,super 应该被用到整个类的层次结构中。

但是,有时这种层次结构的一部分位于第三方代码中,我们无法确定外部包的这些代码中是否使用 super(),因此,当需要对某个第三方类进行子类化时,最好查看其内部代码以及 MRO 中其他类的内部代码。

不同种类的参数

使用 super 的另一个问题是初始化过程中的参数传递。如果没有相同的签名,一个类怎么能调用其基类的 __init__() 代码呢?这会导致下列问题:

class commonBase:
    def __init__(self):
        print("commonBase")
        super().__init__()
class base1(commonBase):
    def __init__(self):
        print("base1")
        super().__init__()
class base2(commonBase):
    def __init__(self):
        print("base2")
        super().__init__()
class myClass(base1,base2):
    def __init__(self,arg):
        print("my base")
        super().__init__(arg)
myClass(10)

运行结果为:

my base
Traceback (most recent call last):
  File "C:\Users\mengma\Desktop\demo.py", line 20, in <module>
    myClass(10)
  File "C:\Users\mengma\Desktop\demo.py", line 19, in __init__
    super().__init__(arg)
TypeError: __init__() takes 1 positional argument but 2 were given

一种解决方法是使用 *args 和 **kwargs 包装的参数和关键字参数,这样即使不使用它们,所有的构造函数也会传递所有参数,如下所示:

class commonBase:
    def __init__(self,*args,**kwargs):
        print("commonBase")
        super().__init__()
class base1(commonBase):
    def __init__(self,*args,**kwargs):
        print("base1")
        super().__init__(*args,**kwargs)
class base2(commonBase):
    def __init__(self,*args,**kwargs):
        print("base2")
        super().__init__(*args,**kwargs)
class myClass(base1,base2):
    def __init__(self,arg):
        print("my base")
        super().__init__(arg)
myClass(10)

运行结果为:

my base
base1
base2
commonBase

不过,这是一种很糟糕的解决方法,由于任何参数都可以传入,所有构造函数都可以接受任何类型的参数,这会导致代码变得脆弱。另一种解决方法是在 MyClass 中显式地使用特定类的 __init__() 调用,但这无疑会导致第一种错误。

总结

现在我们知道:supper获取的是MRO列表中的下一个类,当前类的父类没有实质性的关系;还有如何查看MRO列表。最后需要注意的是super以及MRO列表,针对都是Python新式类!

英语好的话可以读一下这边文章Python's super() considered super

到此这篇关于python中super()函数的理解与基本使用的文章就介绍到这了,更多相关python中super()函数内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python爬取Coursera课程资源的详细过程
Nov 04 Python
python网络编程之文件下载实例分析
May 20 Python
举例讲解Python设计模式编程的代理模式与抽象工厂模式
Jan 16 Python
利用Anaconda完美解决Python 2与python 3的共存问题
May 25 Python
Python实现的连接mssql数据库操作示例
Aug 17 Python
快速解决vue.js 模板和jinja 模板冲突的问题
Jul 26 Python
Python如何调用外部系统命令
Aug 07 Python
Python 日期区间处理 (本周本月上周上月...)
Aug 08 Python
Python实现密码薄文件读写操作
Dec 16 Python
python3 实现调用串口功能
Dec 26 Python
python操作gitlab API过程解析
Dec 27 Python
python学习之使用Matplotlib画实时的动态折线图的示例代码
Feb 25 Python
python自动化操作之动态验证码、滑动验证码的降噪和识别
Aug 30 #Python
Python图片验证码降噪和8邻域降噪
Aug 30 #Python
Python音乐爬虫完美绕过反爬
Aug 30 #Python
详解解Django 多对多表关系的三种创建方式
Aug 23 #Python
一些让Python代码简洁的实用技巧总结
Aug 23 #Python
一篇文章搞懂python混乱的切换操作与优雅的推导式
Aug 23 #Python
Python学习开发之图形用户界面详解
Aug 23 #Python
You might like
晶体管单管来复再生式收音机
2021/03/02 无线电
file_get_contents获取不到网页内容的解决方法
2013/03/07 PHP
ThinkPHP3.1新特性之多数据库操作更加完善
2014/06/19 PHP
php制作动态随机验证码
2015/02/12 PHP
PHP实现的一致性哈希算法完整实例
2015/11/14 PHP
Yii净化器CHtmlPurifier用法示例(过滤不良代码)
2016/07/15 PHP
php实现的AES加密类定义与用法示例
2018/01/29 PHP
js 颜色选择器(兼容firefox)
2009/03/05 Javascript
Extjs中的GridPanel隐藏列会显示在menuDisabled中解决方法
2013/01/27 Javascript
Google Dart编程语法和基本类型学习教程
2013/11/27 Javascript
jquery中子元素和后代元素的区别示例介绍
2014/04/02 Javascript
JavaScript中的索引数组、关联数组和静态数组、动态数组讲解
2014/11/08 Javascript
微信支付 JS API支付接口详解
2016/07/11 Javascript
AngularJS基础 ng-keypress 指令简单示例
2016/08/02 Javascript
js导出excel文件的简洁方法(推荐)
2016/11/02 Javascript
Bootstrap下拉菜单更改为悬停(hover)触发的方法
2017/05/24 Javascript
vue router 跳转后回到顶部的实例
2018/08/31 Javascript
手淘flexible.js框架使用和源代码讲解小结
2018/10/15 Javascript
微信小程序非跳转式组件授权登录的方法示例
2019/05/22 Javascript
[02:36]DOTA2亚洲邀请赛小组赛精彩集锦:奇迹哥卡尔秀翻全场
2017/03/28 DOTA
Python列表list数组array用法实例解析
2014/10/28 Python
Python实现的下载8000首儿歌的代码分享
2014/11/21 Python
Python连接phoenix的方法示例
2017/09/29 Python
python使用udp实现聊天器功能
2018/12/10 Python
pytorch进行上采样的种类实例
2020/02/18 Python
Django单元测试中Fixtures用法详解
2020/02/25 Python
Pycharm Available Package无法显示/安装包的问题Error Loading Package List解决
2020/09/18 Python
python ssh 执行shell命令的示例
2020/09/29 Python
css3旋转木马_动力节点Java学院整理
2017/07/12 HTML / CSS
MATCHESFASHION.COM法国官网:英国奢侈品零售商
2018/01/04 全球购物
Java中各种基本数据类型的默认值都是什么
2016/12/22 面试题
医学检验专业大学生求职信
2013/11/18 职场文书
求职简历中自我评价
2014/01/28 职场文书
2016年清明节寄语
2015/12/04 职场文书
聘任书的格式及模板
2019/10/28 职场文书
SQLServer权限之只开启创建表权限
2022/04/12 SQL Server