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使用xlrd模块操作Excel数据导入的方法
May 26 Python
使用python检测主机存活端口及检查存活主机
Oct 12 Python
python编程实现12306的一个小爬虫实例
Dec 27 Python
Python实现的当前时间多加一天、一小时、一分钟操作示例
May 21 Python
python  Django中的apps.py的目的是什么
Oct 15 Python
Python Excel处理库openpyxl使用详解
May 09 Python
Python实现非正太分布的异常值检测方式
Dec 09 Python
python绘制彩虹图
Dec 16 Python
python字符串替换re.sub()实例解析
Feb 09 Python
python中使用paramiko模块并实现远程连接服务器执行上传下载功能
Feb 29 Python
pycharm的python_stubs问题
Apr 08 Python
对python中arange()和linspace()的区别说明
May 03 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
动漫女神老婆无限好,但日本女生可能就不是这么一回事了!
2020/03/04 日漫
Banner程序
2006/10/09 PHP
PHP字符编码问题之GB2312 VS UTF-8解决方法
2011/06/23 PHP
php compact 通过变量创建数组
2016/11/15 PHP
ThinkPhP+Apache+PHPstorm整合框架流程图解
2020/11/23 PHP
javascript:以前写的xmlhttp池,代码
2008/05/18 Javascript
javascript 学习之旅 (2)
2009/02/05 Javascript
javascript动画浅析
2012/08/30 Javascript
JavaScript的漂亮的代码片段
2013/06/05 Javascript
js控制页面控件隐藏显示的两种方法介绍
2013/10/09 Javascript
javascript初学者常用技巧
2014/09/02 Javascript
javascript实现校验文件上传控件实例
2015/04/20 Javascript
AngularJS入门教程之静态模板详解
2016/08/18 Javascript
vue-cli 默认路由再子路由选中下的选中状态问题及解决代码
2018/09/06 Javascript
JavaScript队列结构Queue实现过程解析
2020/03/07 Javascript
three.js着色器材质的内置变量示例详解
2020/08/16 Javascript
python实现输入数字的连续加减方法
2018/06/22 Python
详解Python3.6的py文件打包生成exe
2018/07/13 Python
python程序控制NAO机器人行走
2019/04/29 Python
python实现简单图书管理系统
2019/11/22 Python
解决python打开https出现certificate verify failed的问题
2020/09/03 Python
Django创建一个后台的基本步骤记录
2020/10/02 Python
CSS3盒子模型详解
2013/04/24 HTML / CSS
CSS3的RGBA中关于整数和百分比值的转换
2015/08/04 HTML / CSS
洛杉矶生活休闲而精致的基础品牌:Mika Jaymes
2018/01/07 全球购物
static关键字的用法
2013/10/07 面试题
医药大学生求职简历的自我评价
2013/10/17 职场文书
迟到检讨书900字
2014/01/14 职场文书
实习生求职自荐信
2014/02/07 职场文书
负责人任命书范本
2014/06/04 职场文书
战略合作意向书
2014/07/29 职场文书
2014年数学教研组工作总结
2014/12/06 职场文书
邀请函模板
2015/02/02 职场文书
2015年教学副校长工作总结
2015/07/22 职场文书
跟班学习心得体会(共6篇)
2016/01/23 职场文书
Python进程间的通信之语法学习
2022/04/11 Python