小白都能理解的Python多继承
本文主要做科普用,在真实编程中不建议使用多重继承,或者少用多重继承,避免使代码难以理解。
方法解析顺序(MRO)
关于多重继承,比较重要的是它的方法解析顺序(可以理解为类的搜索顺序),即MRO。这个跟类是新式类还是经典类有关,因为两者的搜索算法不同。
在Python2及以前的版本,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于新式类;反之,不由任意内置类型派生出的类,则称之为经典类
在Python3以后,没有该区分,所有的类都派生自内置类型object,不管有没有显式继承object,都属于新式类。
对于经典类,多重继承的MRO是深度优先,即从下往上搜索;新式类的MRO是采用C3算法(不同情况下,可表现为广度优先,也可表现为深度优先)。
C3算法表现为深度优先的例子:
# C3-深度优先(D -> B -> A -> C)
class A:
var = 'A var'
class B(A):
pass
class C:
var = 'C var'
class D(B, C):
pass
if __name__ == '__main__':
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>]
print(D.mro())
# A var
print(D.var)
复制代码
C3算法表现为广度优先的例子:
# C3-广度优先(D -> B -> C -> A)
class A:
var = 'A var'
class B(A):
pass
class C(A):
var = 'C var'
class D(B, C):
pass
if __name__ == '__main__':
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
print(D.mro())
# C var
print(D.var)
复制代码
注:关于C3的详细算法本文不讨论,因为我也搞不懂(狗头保命)
菱形多重继承
其实菱形多重继承上面已经有例子了,就是C3算法表现为广度优先这个例子,画起图来是这样的:
菱形多重继承会导致的一个问题是A初始化了两次,如下:
class A:
def say(self):
print("A say")
class B(A):
def say(self):
print("B say")
A.say(self)
class C(A):
def say(self):
print("C say")
A.say(self)
class D(B, C):
def say(self):
print("D say")
B.say(self)
C.say(self)
if __name__ == '__main__':
dd = D()
dd.say()
复制代码
如果只想调用一次A,可使用super方法:
class A:
def say(self):
print("A say")
class B(A):
def say(self):
print("B say")
super().say()
class C(A):
def say(self):
print("C say")
super().say()
class D(B, C):
def say(self):
print("D say")
super().say()
if __name__ == '__main__':
print(D.mro())
dd = D()
dd.say()
复制代码
_init_ 与 super()
1.如果父类有init方法,子类没有,则子类默认继承父类的init方法
class A:
def __init__(self, a1, a2):
self.a1 = a1
self.a2 = a2
def say(self):
print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
class B(A):
def say(self):
print("B say, a1: %s, a2: %s" % (self.a1, self.a2))
if __name__ == '__main__':
# 因为B继承了A的init方法,所以也要传入 a1,a2参数
bb = B("10", "100")
bb.say()
复制代码
2.如果父类有init方法,子类也有,可理解为子类重写了父类的init方法
class A:
def __init__(self, a1, a2):
self.a1 = a1
self.a2 = a2
def say(self):
print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
class B(A):
def __init__(self, b1):
self.b1 = b1
def say(self):
print("B say, b1: %s" % self.b1)
if __name__ == '__main__':
# 此处B重写了A的init方法,所以只需要传入b1参数,也没有拥有a1,a2属性
bb = B("10")
bb.say()
复制代码
3.对于第二点,为了能使用或者扩展父类的行为,更常见的做法是在重写init方法的同时,显示调用父类的init方法(意味传的参数要变成3个)。
# 三种写法
class A:
def __init__(self, a1, a2):
self.a1 = a1
self.a2 = a2
def say(self):
print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
class B(A):
def __init__(self, b1, a1, a2):
# 第一种写法: Python2的写法
# super(B, self).__init__(a1, a2)
# 第二种写法(推荐):Python3的写法,与第一种等价
super().__init__(a1, a2)
# 第三种写法:与前两种等价,不过这种需要显示调用基类,而第二种不用
# A.__init__(self, a1, a2)
self.b1 = b1
def say(self):
print("B say, a1: %s, a2: %s, b1: %s" % (self.a1, self.a2, self.b1))
if __name__ == '__main__':
# 此处B重写了A的init方法,所以只需要传入b1参数,也没有拥有a1,a2属性
bb = B("10", "100", "1000")
bb.say()
复制代码
最后的提醒
注意 __init__ 方法不要写错,避免写成__ini__或者其他的,因为两个是在太像,出了问题很难排查(坑过两次)。
本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理
想要获取更多Python学习资料可以加QQ:2955637827私聊或加Q群630390733大家一起来学习讨论吧!
小白都能理解的Python多继承的更多相关文章
- 图解Go协程调度原理,小白都能理解
阅读本文仅需五分钟,golang协程调度原理,小白也能看懂,超实用. 什么是协程 对于进程.线程,都是有内核进行调度,有CPU时间片的概念,进行抢占式调度.协程,又称微线程,纤程.英文名Corouti ...
- 小白都能理解的TCP三次握手四次挥手
前言 TCP在学习网络知识的时候是经常的被问到知识点,也是程序员必学的知识点,今天小杨用最直白的表述带大家来认识认识,喜欢的朋友记得点点关注哈. 何为TCP 上点官方的话:是一种面向连接(连接导向)的 ...
- python基础——继承和多态
python基础——继承和多态 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类.父类或超类 ...
- python类继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过 继承 机制.继承完全可以理解成类之间的 类型和子类型 关系. 假设你想要写一个程序来记录学校之中的教师和学生情况.他们有一些 ...
- sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO
sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO 今天在弄一个 sqlalchemy 的数据库基类的时候,遇到了跟多继承相关的一个小问题,因此顺便看了一 ...
- Python进阶-继承中的MRO与super
Python进阶-继承中的MRO与super 写在前面 如非特别说明,下文均基于Python3 摘要 本文讲述Python继承关系中如何通过super()调用"父类"方法,supe ...
- python基础——继承实现的原理
python基础--继承实现的原理 1 继承顺序 class A(object): def test(self): print('from A') class B(A): def test(self) ...
- python基础——继承与派生、组合
python基础--继承与派生 1 什么是继承: 继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类成为基类或超累,新建的类成为派生类或子类 1.1 继承分为:单 ...
- python 面向对象 继承 派生 组合
具体参考博客:http://www.cnblogs.com/linhaifeng/articles/6182264.html#_label12 一.面向对象 面向对象:对象与对象之间的相互交互,不可预 ...
随机推荐
- jQuery 第一章 $()选择器
jquery 是什么? jquery 其实就是一堆的js函数(js库),也是普通的js而已. 有点像我们封装一个函数,把他放到单独的js 文件,等待有需要的时候调用它. 那么使用它有啥好处呢? jqu ...
- 通过PHPExcel将Excel表文件中数据导入数据库
1 <?php 2 header("Content-Type:text/html;charset=utf-8"); 3 include_once 'PHPExcel.php' ...
- C++之父接受采访:对 C++ 成功的关键和发展历程进行了回顾
C++ 的起源可以追溯到 40 年前,但它仍然是当今使用最广泛的编程语言之一. 到 2020 年 9 月为止,C++ 是仅次于 C 语言.Java 和 Python,位于全球第四的编程语言.根据最新的 ...
- java学生简单管理系统
1 //设一个班有n名学生,期末考试5门,编写程序评定学生奖学金 2 514 //要求打印输出一二等奖学金学生的学号,姓名和各科成绩 3 515 //总成绩超过全班总平均成绩20%一等奖学金,超过总平 ...
- 九. Vuex详解
1. 理解Vuex 1.1 Vuex功能 官方解释 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用 集中式存储 管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方 ...
- SQL Server常用函数及命令
1.字符串函数 --ascii函数,返回字符串最左侧字符的ascii码值 SELECT ASCII('a') AS asciistr --ascii代码转换函数,返回指定ascii值对应的字符 SEL ...
- SQL直接生成实体属性,简单粗暴型
在java开发中,不可避免的要碰到根据表生成对应的实体,这个过程是比较机器且繁琐的,我也用过一些逆向工程的工具,比如IDEA自带的生成实体,还有网上开源的工具,用起来也是可以的. 我现在开发用的持久层 ...
- 第 4篇 Scrum 冲刺博客
一.站立式会议 1.站立式会议照片 2.昨天已完成的工作 登录信息的匹配 3.今天计划完成的工作 ①售货员页面功能 ②销售排行 4.工作中遇到的困难 ①页面按钮太过复杂,逻辑错乱 ②有的同学数据库驱动 ...
- AcWing 404. 婚礼
大型补档计划 题目链接 根据题意,显然只有新郎这边可能存在矛盾,考虑这边怎么放即可,新娘那边的放法与这边正好相反且一一对应. 显然对于两个约束条件是一对矛盾,开始我以为可以用并查集,后来发现输出方案的 ...
- 【SDOI2017】天才黑客(前后缀优化建图 & 最短路)
Description 给定一张有向图,\(n\) 个点,\(m\) 条边.第 \(i\) 条边上有一个边权 \(c_i\),以及一个字符串 \(s_i\). 其中字符串 \(s_1, s_2, \c ...