Python - 面向对象编程 - super()
前置知识
为什么要用 super()
当子类重写了父类方法时,又想调用父类的同名方法时,就需要用到 super()
什么是 super
- 在 Python 中,super 是一个特殊的类
- super() 就是使用 super 类创建出来的对象
- 实际应用的场景:子类在重写父类方法时,调用父类方法
单继承中使用 super
实例方法使用 super
类图
实际代码
class A:
def __init__(self):
self.n = 1 def add(self, m):
print(f'AAA [self] is {id(self)}')
print(f'AAA [self.n] is {self.n}')
self.n += m class B(A):
def __init__(self):
self.n = 100 # 重写父类方法
def add(self, m):
# 子类特有代码
print(f'BBB [self] is {id(self)}')
print(f'BBB [self.n] is {self.n}') # 调用父类方法
super().add(m) self.n += m b = B()
b.add(2)
print(b.n) # 输出结果
BBB [self] is 4489158560
BBB [self.n] is 100 AAA [self] is 4489158560
AAA [self.n] is 100 104
- super().add() 的确调用了父类方法
- 重点:此时父类方法的 self 并不是父类实例对象,而是子类实例对象
构造方法使用 super
class Animal:
def __init__(self, name):
self.name = name def prints(self):
print("Animale name is ", self.name) class Dog(Animal):
def __init__(self, name, age):
# 调用父类的 init 构造方法
super(Dog, self).__init__(name)
self.age = age def prints(self):
# 调用父类的方法
super(Dog, self).prints()
print("Dog age is ", self.age) dog = Dog("小汪", 10)
dog.prints() # 输出结果
Animale name is 小汪
Dog age is 10
这里用了 super(子类名, self) ,和上面的 super() 是一样效果
调用父类方法有两种方式
- super().父类方法()
- super(子类名, self).父类方法()
其实还有第三种
在 Python 2.x 的时候,如果需要调用父类的方法,还可以用
父类名.方法(self)
- 这种方式,Python 3.x 还是支持的
- 不过不推荐,因为父类名发生变化的话,方法调用位置的类名也要同步修改
通过父类名调用父类方法(不推荐)
class Animal:
def __init__(self, name):
self.name = name def prints(self):
print("Animale name is ", self.name) class Dog(Animal):
def __init__(self, name, age):
# 调用父类的 init 构造方法
Animal.__init__(self, name)
self.age = age def prints(self):
# 调用父类的方法
Animal.prints(self)
print("Dog age is ", self.age) dog = Dog("小汪", 10)
dog.prints() # 输出结果
Animale name is 小汪
Dog age is 10
通过父类名调用的这种方式,是需要传 self 参数的哦
温馨提示
在开发时, 父类名.方法() , super().方法() 两种方式不要混用哈
灵魂拷问一:既然已经重写了子类的构造方法,为什么还要去调用 super?
子类需要重写父类方法来实现子类独有的功能,但同时又需要依赖父类方法来完成某些逻辑
实际栗子
- 在实现多线程的时候(后面会详细展开说多线程)
- 父类 Thread 的构造方法包含了很多逻辑代码
- 子线程虽然需要实现子类独有功能,但仍需父类方法来处理其他逻辑
from threading import Thread class MyThread(Thread):
def __init__(self, name):
# 1、实现子类独有功能
print("子类线程 %s" % name)
# 2、需要依赖父类方法完成其他功能
super().__init__(name=name)
多继承中使用 super
类图
实际代码
# 多继承
class Animal:
def __init__(self, animalName):
print(animalName, 'is an animal.') # Mammal 继承 Animal
class Mammal(Animal):
def __init__(self, mammalName):
print(mammalName, 'is a mammal.')
super().__init__(mammalName) # CannotFly 继承 Mammal
class CannotFly(Mammal):
def __init__(self, mammalThatCantFly):
print(mammalThatCantFly, "cannot fly.")
super().__init__(mammalThatCantFly) # CannotSwim 继承 Mammal
class CannotSwim(Mammal):
def __init__(self, mammalThatCantSwim):
print(mammalThatCantSwim, "cannot swim.")
super().__init__(mammalThatCantSwim) # Cat 继承 CannotSwim 和 CannotFly
class Cat(CannotSwim, CannotFly):
def __init__(self):
print('I am a cat.');
super().__init__('Cat') # Driver code
cat = Cat()
print('')
bat = CannotSwim('Bat') # 输出结果
I am a cat.
Cat cannot swim.
Cat cannot fly.
Cat is a mammal.
Cat is an animal. Bat cannot swim.
Bat is a mammal.
Bat is an animal.
好像挺奇怪的,从输出结果看,为什么 CannotSwim 类里面的 super().__init__() 调用的是 CannotFly 类里面的方法呢?不是应该调用 CannotSwim 的父类 Mamal 的方法吗?
灵魂拷问二:super 的执行顺序到底是什么?
- 其实 super() 并不一定调用父类的方法
- super() 是根据类的 MRO 方法搜索顺序来决定调用谁的
- super() 真正调用的是 MRO 中的下一个类,而不一定是父类
- 当然,这种情况只会出现在多继承
先来看看 Cat 的 MRO
print(Cat.__mro__) (<class '__main__.Cat'>, <class '__main__.CannotSwim'>, <class '__main__.CannotFly'>, <class '__main__.Mammal'>, <class '__main__.Animal'>, <class 'object'>)
从 Cat 的 MRO 可以看到
- CannotSwim 后面跟的是 CannotFly 而不是 Mamal
- 所以 CannotSwim 类里面的 super() 会调用 CannotFly 里面的方法
多继承的栗子二
实际代码
class A:
def __init__(self):
self.n = 2 def add(self, m):
# 第四步
# 来自 D.add 中的 super
# self == d, self.n == d.n == 5
print('self is {0} @AAA.add'.format(self))
self.n += m
# d.n == 7 class C(A):
def __init__(self):
self.n = 4 def add(self, m):
# 第三步
# 来自 B.add 中的 super
# self == d, self.n == d.n == 5
print('self is {0} @CCC.add'.format(self))
# 等价于 suepr(C, self).add(m)
# self 的 MRO 是 [D, B, C, A, object]
# 从 C 之后的 [A, object] 中查找 add 方法
super().add(m) # 第五步
# d.n = 7
self.n += 4
# d.n = 11 class B(A):
def __init__(self):
self.n = 3 def add(self, m):
# 第二步
# 来自 D.add 中的 super
# self == d, self.n == d.n == 5
print('self is {0} @BBB.add'.format(self))
# self 的 MRO 是 [D, B, C, A, object]
# 从 B 之后的 [C, A, object] 中查找 add 方法
# 从 C 找 add 方法
super().add(m) # 第六步
# d.n = 11
self.n += 3
# d.n = 14 class D(B, C):
def __init__(self):
self.n = 5 def add(self, m):
# 第一步
print('self is {0} @DDD.add'.format(self))
# self 的 MRO 是 [D, B, C, A, object]
# 从 D 之后的 [B, C, A, object] 中查找 add 方法
# 从 B 找 add 方法
super().add(m) # 第七步
# d.n = 14
self.n += 5
# self.n = 19 d = D()
d.add(2)
print(d.n)
先看看 D 类的 MRO
print(D.__mro__) (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
输出结果
self is <__main__.D object at 0x10c14a190> @DDD.add
self is <__main__.D object at 0x10c14a190> @BBB.add
self is <__main__.D object at 0x10c14a190> @CCC.add
self is <__main__.D object at 0x10c14a190> @AAA.add
19
调用顺序的确是 D、B、C、A
执行顺序
class D(B, C): class B(A): class C(A): class A:
def add(self, m): def add(self, m): def add(self, m): def add(self, m):
super().add(m) 1.---> super().add(m) 2.---> super().add(m) 3.---> self.n += m
self.n += 5 <------6. self.n += 3 <----5. self.n += 4 <----4. <--|
(14+5=19) (11+3=14) (7+4=11) (5+2=7)
执行顺序图
Python - 面向对象编程 - super()的更多相关文章
- python 面向对象编程学习
1. 问题:将所有代码放入一个py文件:无法维护 方案:如果将代码才分放到多个py文件,好处: 1. 同一个名字的变量互相不影响 2.易于维护 3.引用模块: import module 2.包:解决 ...
- python面向对象编程进阶
python面向对象编程进阶 一.isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 1 ...
- Python面向对象编程——继承与派生
Python面向对象编程--继承与派生 一.初始继承 1.什么是继承 继承指的是类与类之间的关系,是一种什么"是"什么的关系,继承的功能之一就是用来解决代码重用问题. 继承是一种创 ...
- 图解python | 面向对象编程
作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/56 本文地址:http://www.showmeai.tech/article-det ...
- python 面向对象编程(一)
一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法. 类是对现实世界中一些事物的封装,定义一个类可以采用下面的方式来定义: class c ...
- Python面向对象编程指南
Python面向对象编程指南(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1SbD4gum4yGcUruH9icTPCQ 提取码:fzk5 复制这段内容后打开百度网 ...
- Python面向对象编程(下)
本文主要通过几个实例介绍Python面向对象编程中的封装.继承.多态三大特性. 封装性 我们还是继续来看下上文中的例子,使用Student类创建一个对象,并修改对象的属性.代码如下: #-*- cod ...
- Python 面向对象编程——访问限制
<无访问限制的对象> 在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑.但是,从前面Student类的定义来看(见:Py ...
- Python 面向对象编程 继承 和多态
Python 面向对象编程 继承 和多态 一:多继承性 对于java我们熟悉的是一个类只能继承一个父类:但是对于C++ 一个子类可以有多个父亲,同样对于 Python一个类也可以有多个父亲 格式: c ...
随机推荐
- 理清 UT、UTC、GMT和CST
平时各种时间标准简写绕得不明不白,查一下整理在这里. IAT: International Atomic Time, 原子时.又称国际原子时,是一种通过原子钟得到的时间标准,原子钟是世界上已知最准确的 ...
- 定时任务quartz
pom引入 <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>qua ...
- VS Code的插件安装位置改变
VS Code的相关配置 VS Code的插件安装位置改变 可以通过创建连接,将默认的extensions位置,改变到D盘 Windows 链接彻底解决 vscode插件安装位置问题 mklink / ...
- Mac使用Charles抓取ios手机APP中的https请求
1.配置Http代理 Port为监听端口号,默认为8888,勾选Enable transparent HTTP proxying,接着勾选SOCKS proxy,可以监听Socks请求 2.安装Cha ...
- URI 未注册(设置 | 语言和框架 | 架构和 DTD)
创建xml文件导入资源出错 解决方法:点击左边的小红灯,选择获取外部资源,加载资源即可
- 一文彻底搞清 Gradle 依赖【转】
来源:曾是放牛娃 www.jianshu.com/p/59fd653a54d2 转自:https://mp.weixin.qq.com/s?__biz=MzA3MDMyMjkzNg==&mid ...
- CentOS 8.0 安装docker 报错:Problem: package docker-ce-3:19.03.4-3.el7.x86_64 requires containerd.io >= 1
1.错误内容 package docker-ce-3:19.03.2-3.el7.x86_64 requires containerd.io >= 1.2.2-3, but none of th ...
- NDIS LWF:NdisFSendNetBufferLists蓝屏(DRIVER_IRQL_NOT_EQUAL_OR_LESS)
调用NdisFSendNetBufferLists发送自定义数据包后蓝屏,蓝屏代码为DRIVER_IRQL_NOT_EQUAL_OR_LESS,如果创建的NBL都没问题,一定要确保该自定义的NBL要在 ...
- AQS学习(一)自旋锁原理介绍(为什么AQS底层使用自旋锁队列?)
1.什么是自旋锁? 自旋锁作为锁的一种,和互斥锁一样也是为了在并发环境下保护共享资源的一种锁机制.在任意时刻,只有一个执行单元能够获得锁. 互斥锁通常利用操作系统提供的线程阻塞/唤醒机制实现,在争用锁 ...
- 英语学习Tips
[edp] 写英语论文那些容易忽略的点: 连字符大小写问题 连字符"-"后首字母的大小写_周树森_新浪博客 (sina.com.cn) reference list 里面的书名要斜 ...