super() 的入门使用

在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了。

调用父类同名方法有两种方式:

1、调用未绑定的父类方法

2、使用super函数来调用

先来说下第一种方式:调用未绑定的父类方法。

演示:

class Base(object):
def greet(self):
print('hi,I am Base') class A(Base):
def greet(self):
Base.greet(self) #通过父类Base直接调用greet方法,并把self作为参数
print('hi,I am A') a=A() a.greet()

运行结果:

C:\python36\python.exe E:/demo/testPyQt.py
hi,I am Base
hi,I am A Process finished with exit code 0

这种方式简单用还可以,如果在多重继承中就会有问题。

演示:

class Base(object):
def __init__(self):
print("enter Base")
print("leave Base") class A(Base):
def __init__(self):
print("enter A")
Base.__init__(self) #调用父类的构造函数进行初始化
print("leave A") class B(Base):
def __init__(self):
print("enter B")
Base.__init__(self) #调用父类的构造函数进行初始化
print("leave B") class C(A,B):
def __init__(self):
print("enter C")
A.__init__(self) #调用父类A的构造函数进行初始化
B.__init__(self) #调用父类B的构造函数进行初始化
print("leave C") c=C()

运行结果:

C:\python36\python.exe E:/demo/testPyQt.py
enter C
enter A
enter Base
leave Base
leave A
enter B
enter Base
leave Base
leave B
leave C Process finished with exit code 0

从上面的运行结果可以看出,基类Base的构造函数被调用了两次,这是有问题的,正常的应该是:A的构造函数调用一次,B的构造函数调用一次,基类Base的构造函数调用一次。

那么这个问题能不能解决呢,可以的,下面介绍super。

演示:

class Animal(object):
def __init__(self, name):
self.name = name def greet(self):
print('Hello, I am %s.' % self.name) class Dog(Animal):
def greet(self):
super(Dog, self).greet() #调用父类Animal的greet方法
print('WangWang...') d=Dog("xiaohuang") d.greet()

在上面,Animal 是父类,Dog 是子类,我们在 Dog 类重定义了 greet 方法,为了能同时实现父类的功能,我们又调用了父类的方法,运行结果:

C:\python36\python.exe E:/demo/testPyQt.py
Hello, I am xiaohuang.
WangWang... Process finished with exit code 0

super 的一个最常见用法可以说是在子类中调用父类的初始化方法了。

演示:

class Base(object):
def __init__(self, a, b):
self.a = a
self.b = b class A(Base):
def __init__(self, a, b, c):
super(A, self).__init__(a, b) # Python3 可使用 super().__init__(a, b)
self.c = c a=A(100,200,300) print("a=%d, b=%d, c=%d" % (a.a,a.b,a.c))

运行结果:

C:\python36\python.exe E:/demo/testPyQt.py
a=100, b=200, c=300 Process finished with exit code 0

深入super()

看了上面的使用,你可能会觉得 super 的使用很简单,无非就是获取了父类,并调用父类的方法。其实,在上面的情况下,super 获得的类刚好是父类,但在其他情况就不一定了,super 其实和父类没有实质性的关联。

让我们看一个稍微复杂的例子,涉及到多重继承,代码如下:

class Base(object):
def __init__(self):
print("enter Base")
print("leave Base") class A(Base):
def __init__(self):
print("enter A")
super(A,self).__init__()
print("leave A") class B(Base):
def __init__(self):
print("enter B")
super(B,self).__init__()
print("leave B") class C(A,B):
def __init__(self):
print("enter C")
super(C,self).__init__()
print("leave C") c=C()

运行结果:

C:\python36\python.exe E:/demo/testPyQt.py
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C Process finished with exit code 0

OK,这个运行结果是正常的,类C继承自A,B,而A和B又分别继承类Base,每一个类的构造函数分别被调用了一次。

如果你认为 super 代表『调用父类的方法』,那你很可能会疑惑为什么 enter A 的下一句不是 enter Base 而是 enter B。原因是,super 和父类没有实质性的关联,现在让我们搞清 super 是怎么运作的。

MRO列表

事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表:

print(C.mro())

运行结果:

[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]

这个列表真实的列出了类C的继承顺序。C->A->B->Base->object。在方法调用时,是按照这个顺序查找的。

那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法来实现的,这里我们就不去深究这个算法了,感兴趣的读者可以自己去了解一下,总的来说,一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则:

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

super 原理

super 的工作原理如下:

def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]

其中,cls 代表类,inst 代表实例,上面的代码做了两件事:

  1. 获取 inst 的 MRO 列表
  2. 查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]

    当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。

    现在,让我们回到前面的例子。

首先看类 C 的 init 方法:

super(C,self).__init__()

这里的 self 是当前 C 的实例,self.class.mro() 结果是:

[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]

可以看到,C 的下一个类是 A,于是,跳到了 A 的 init,这时会打印出 enter A,并执行下面一行代码:

super(A,self).__init__()

注意,这里的 self 也是当前 C 的实例,MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个类,发现是 B,于是,跳到了 B 的 init,这时会打印出 enter B,而不是 enter Base。

整个过程还是比较清晰的,关键是要理解 super 的工作方式,而不是想当然地认为 super 调用了父类的方法。

小结

  1. 事实上,super 和父类没有实质性的关联。
  2. super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类。(补充:super(cls, inst)获得的是在inst所属类的MRO列表中排在cls后面的那个类。(因为MRO列表只有类才有的,实例没有MRO列表))

补充:

如果A和B不是来自同一个Base,

class Base1(object):
def __init__(self):
print("enter Base1")
print("leave Base1") class Base2(object):
def __init__(self):
print("enter Base2")
print("leave Base2") class A(Base1):
def __init__(self):
print("enter A")
super().__init__()
print("leave A") class B(Base2):
def __init__(self):
print("enter B")
super().__init__()
print("leave B") class C(A, B):
def __init__(self):
print("enter C")
super().__init__()
print("leave C") 发现, B和Base2没有被初始化:
enter C
enter A
enter Base1
leave Base1
leave A
leave C 不漏掉父类的初始化, Base1 / Base2也要调用
super(): class Base1(object):
def __init__(self):
print("enter Base1")
super().__init__()
print("leave Base1") class Base2(object):
def __init__(self):
print("enter Base2")
super().__init__()
print("leave Base2") super才不会掉链子.

原文链接:https://blog.csdn.net/wo198711203217/article/details/84097274

彻底搞懂python super函数的作用的更多相关文章

  1. (转)从一道面试题彻底搞懂hashCode与equals的作用与区别及应当注意的细节

    背景:学习java的基础知识,每次回顾,总会有不同的认识.该文系转载 最近去面试了几家公司,被问到hashCode的作用,虽然回答出来了,但是自己还是对hashCode和equals的作用一知半解的, ...

  2. super函数的作用

    super函数的作用super().__init__()当子类重写父类的方法时,会覆盖父类方法,super此举是保留父类 如果属性名跟方法名相同,属性会覆盖方法 方法必须要有实例才能被调用,这叫做绑定

  3. 搞懂Python正则表达式,这一篇就够了

    本文代码基于Python3.11解释器,除了第一次示例,代码将省略 import re 这个语句 所有示例代码均可以在我的github仓库中的 code.py文件内查看 [我的仓库](PythonLe ...

  4. Python super() 函数的概念和例子

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

  5. Python super() 函数

    super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果重定义某个方法,该方法会覆盖父类的同名方法,但有时 ...

  6. python super()函数详解

    引言: 在类的多继承使用场景中,重写父类的方法时,可能会考虑到需要重新调用父类的方法,所以super()函数就是比较使用也很必要的解决方法: 文章来源: http://www.cnblogs.com/ ...

  7. 一文搞懂Python迭代器和生成器

    很多童鞋搞不懂python迭代器和生成器到底是什么?它们之间又有什么样的关系? 这篇文章就是要用最简单的方式让你理解Python迭代器和生成器! 1.迭代器和迭代过程 维基百科解释道: 在Python ...

  8. python super()函数:调用父类的构造方法

    python子类会继承父类所有的类属性和类方法.严格来说,类的构造方法其实就是实例方法,因此,父类的构造方法,子类同样会继承. 我们知道,python是一门支持多继承的面向对象编程语言,如果子类继承的 ...

  9. Python setattr() 函数 ,Python super() 函数: Python 内置函数 Python 内置函数

    描述 setattr 函数对应函数 getatt(),用于设置属性值,该属性必须存在. 语法 setattr 语法: setattr(object, name, value) 参数 object -- ...

  10. 一文搞懂Python函数(匿名函数、嵌套函数、闭包、装饰器)!

    Python函数定义.匿名函数.嵌套函数.闭包.装饰器 目录 Python函数定义.匿名函数.嵌套函数.闭包.装饰器 函数核心理解 1. 函数定义 2. 嵌套函数 2.1 作用 2.2 函数变量作用域 ...

随机推荐

  1. c# - 如何在圆角 WPF 窗体中创建圆角矩形?

    我正在 WPF 中创建一个应用程序,我想要圆角.收到.现在窗体是无边框的,我正在尝试创建一个圆角矩形并将其放在顶部,使其看起来像 Windows 应用程序的顶部栏. 我做不到. 这是我的代码: < ...

  2. kubenetes中的pod删除策略 级联删除与非级联删除

    StatefulSet 有状态应用[有状态应用] 有状态:StatefulSet - 集群节点之间的关系. - 数据不完全一致. - 实例之间不对等的关系. - 依靠外部存储的应用. - 通过dns维 ...

  3. QuartusII调用 PLL_IP核方法(Mega Wizard)

    [基本信息] 要求:调用PLL-IP核,50Mhz晶振输入,输出四路时钟不同信号:100Mhz,25Mhz,50Mhz(90°相位),50Mhz(20%占空比). 芯片型号:cyclone Ⅳ EP4 ...

  4. vue 的directives一个坑

    大江东去,浪淘尽,千古风流人物.故垒西边,人道是,三国周郎赤壁.乱石穿空,惊涛拍岸,卷起千堆雪.江山如画,一时多少豪杰.遥想公瑾当年,小乔初嫁了,雄姿英发.羽扇纶巾,谈笑间,樯橹灰飞烟灭.故国神游,多 ...

  5. 认识mongodb

    Mongodb是一个介于关系数据库和非关系数据库之间的产品(Nosql),是非关系数据库当中功能最丰富,最像关系数据库的,语法有点类似javascript面向对象的查询语言,它是一个面向集合的,模式自 ...

  6. UDP 发送两遍对比一致能绝对判定发送过程成功传递完整数据吗

    UDP 发送两边对比一致,能确定数据传输无错误吗 对比两条相同数据的MD5 这样做可行吗

  7. 小米 红米 MIUI 重新打开(开启)蓝牙耳机电量弹窗

    原本关闭了下拉通知栏,显示耳机电量的窗口.想重新开开启. 解决方法: 1.确保小爱同学开了通知权限. 2.在蓝牙-高级设置里-开启"小米快连" 3.手机是解锁状态(非锁屏),耳机需 ...

  8. golang sync.Map 与使用普通的 map 的区别

    使用sync.Map与普通的Go map主要有以下几点区别: 1. 并发安全性 普通map: 在没有外部同步的情况下,不是并发安全的.在多goroutine访问时,如果没有适当的锁或其他同步机制保护, ...

  9. SDL3 入门(1):Hello, SDL3!

    在本系列中我们使用 Windows Terminal + Powershell 组合作为我们在 Windows 系统下的终端工具,Windows 11 自带该环境.你也可以使用任意自己喜欢的终端环境代 ...

  10. rest接口list参数接收

    list<String> post传参 ["1112","2222","3332"]list<Object> pos ...