1.   Python的继承以及调用父类成员

python子类调用父类成员有2种方法,分别是普通方法和super方法

假设Base是基类

class Base(object):def __init__(self):print “Base init”

则普通方法如下

class Leaf(Base):def __init__(self):Base.__init__(self)print “Leaf init”

super方法如下

class Leaf(Base):def __init__(self):super(Leaf, self).__init__()print “Leaf init”

在上面的简单场景下,两种方法的效果一致:

>>> leaf = Leaf()

Base init

Leaf init

2.   钻石继承遇到的难题

当我们来到钻石继承场景时,我们就遇到了一个难题:

如果我们还是使用普通方法调用父类成员,代码如下:

class Base(object):def __init__(self):print “Base init”class Medium1(Base):def __init__(self):Base.__init__(self)print “Medium1 init”class Medium2(Base):def __init__(self):Base.__init__(self)print “Medium2 init”class Leaf(Medium1, Medium2):def __init__(self):Medium1.__init__(self)Medium2.__init__(self)print “Leaf init”    

当我们生成Leaf对象时,结果如下:

>>> leaf = Leaf()

Base init

Medium1 init

Base init

Medium2 init

Leaf init

可以看到Base被初始化了两次!这是由于Medium1和Medium2各自调用了Base的初始化函数导致的。

3.   各语言的解决方法

钻石继承中,父类被多次初始化是个非常难缠的问题,我们来看看其他各个语言是如何解决这个问题的:

3.1. C++

C++使用虚拟继承来解决钻石继承问题。

Medium1和Medium2虚拟继承Base。当生成Leaf对象时,Medium1和Medium2并不会自动调用虚拟基类Base的构造函数,而需要由Leaf的构造函数显式调用Base的构造函数。

3.2. Java

Java禁止使用多继承。

Java使用单继承+接口实现的方式来替代多继承,避免了钻石继承产生的各种问题。

3.3. Ruby

Ruby禁止使用多继承。

Ruby和Java一样只支持单继承,但它对多继承的替代方式和Java不同。Ruby使用Mixin的方式来替代,在当前类中mixin入其他模块,来做到代码的组装效果。

3.4. Python

Python和C++一样,支持多继承的语法。但Python的解决思路和C++完全不一样,Python使用的是super

我们把第2章的钻石继承用super重写一下,看一下输出结果

class Base(object):def __init__(self):print “Base init”class Medium1(Base):def __init__(self):super(Medium1, self).__init__()print “Medium1 init”class Medium2(Base):def __init__(self):super(Medium2, self).__init__()print “Medium2 init”class Leaf(Medium1, Medium2):def __init__(self):super(Leaf, self).__init__()print “Leaf init”        

我们生成Leaf对象:

>>> leaf = Leaf()

Base init

Medium2 init

Medium1 init

Leaf init

可以看到整个初始化过程符合我们的预期,Base只被初始化了1次。而且重要的是,相比原来的普通写法,super方法并没有写额外的代码,也没有引入额外的概念

4.   super的内核:mro

要理解super的原理,就要先了解mro。mro是method resolution order的缩写,表示了类继承体系中的成员解析顺序。

在python中,每个类都有一个mro的类方法。我们来看一下钻石继承中,Leaf类的mro是什么样子的:

>>> Leaf.mro()

[<class '__main__.Leaf'>, <class '__main__.Medium1'>, <class '__main__.Medium2'>, <class '__main__.Base'>, <type 'object'>]

可以看到mro方法返回的是一个祖先类的列表。Leaf的每个祖先都在其中出现一次,这也是super在父类中查找成员的顺序。

通过mro,python巧妙地将多继承的图结构,转变为list的顺序结构。super在继承体系中向上的查找过程,变成了在mro中向右的线性查找过程,任何类都只会被处理一次。

通过这个方法,python解决了多继承中的2大难题:

1. 查找顺序问题。从Leaf的mro顺序可以看出,如果Leaf类通过super来访问父类成员,那么Medium1的成员会在Medium2之前被首先访问到。如果Medium1和Medium2都没有找到,最后再到Base中查找。

2. 钻石继承的多次初始化问题。在mro的list中,Base类只出现了一次。事实上任何类都只会在mro list中出现一次。这就确保了super向上调用的过程中,任何祖先类的方法都只会被执行一次。

至于mro的生成算法,可以参考这篇wiki:https://en.wikipedia.org/wiki/C3_linearization

5.   super的具体用法

我们首先来看一下python中的super文档

>>> help(super)

Help on class super in module __builtin__:

class super(object)

|  super(type, obj) -> bound super object; requires isinstance(obj, type)

|  super(type) -> unbound super object

|  super(type, type2) -> bound super object; requires issubclass(type2, type)

光从字面来看,这可以算是python中最语焉不详的帮助文档之一了。甚至里面还有一些术语误用。那super究竟应该怎么用呢,我们重点来看super中的第1和第3种用法

5.1. super(type, obj)

当我们在Leaf的__init__中写这样的super时:

class Leaf(Medium1, Medium2):def __init__(self):super(Leaf, self).__init__()print “Leaf init”

super(Leaf, self).__init__()的意思是说:

  1. 获取self所属类的mro, 也就是[Leaf, Medium1, Medium2, Base]
  2. 从mro中Leaf右边的一个类开始,依次寻找__init__函数。这里是从Medium1开始寻找
  3. 一旦找到,就把找到的__init__函数绑定到self对象,并返回

从这个执行流程可以看到,如果我们不想调用Medium1的__init__,而想要调用Medium2的__init__,那么super应该写成:super(Medium1, self)__init__()

5.2. super(type, type2)

当我们在Leaf中写类方法的super时:

class Leaf(Medium1, Medium2):def __new__(cls):obj = super(Leaf, cls).__new__(cls)print “Leaf new”return obj

super(Leaf, cls).__new__(cls)的意思是说:

  1. 获取cls这个类的mro,这里也是[Leaf, Medium1, Medium2, Base]
  2. 从mro中Leaf右边的一个类开始,依次寻找__new__函数
  3. 一旦找到,就返回“非绑定”的__new__函数

由于返回的是非绑定的函数对象,因此调用时不能省略函数的第一个参数。这也是这里调用__new__时,需要传入参数cls的原因

同样的,如果我们想从某个mro的某个位置开始查找,只需要修改super的第一个参数就行

6.   小结

至此,我们讲解了和super相关的用法及原理,小结一下我们讲过的内容有:

  1. python调用父类成员共有2种方法:普通方法,super方法
  2. 在钻石继承中,普通方法会遇到Base类两次初始化的问题
  3. 简述了其他语言对这个问题的解决方法,并用实例展示了python使用super可以解决此问题
  4. 在讲super具体用法前,先讲了super的内核:mro的知识和原理
  5. 讲解了super两种主要的用法及原理

标签:python, super, mro, 多继承

转载于:https://www.cnblogs.com/testview/p/4651198.html

深入super,看Python如何解决钻石继承难题相关推荐

  1. python---之super()继承,解决钻石继承难题

    1.   Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object):def __init__ ...

  2. Python super钻石继承

    1.   Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object):def __init__ ...

  3. Python super(钻石继承)

    1.   Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object):def __init__ ...

  4. 利用python画钻石_python入门 -- 钻石继承(菱形继承)

    在面向对象(OOP)编程中,很多情况下会遇到多继承和多重继承的问题和坑,这里带对大家 认识一下其中的一个钻石继承(菱形继承)的问题. 什么时候会出现钻石继承(菱形继承)呢? 当在类树中,由多个类共享同 ...

  5. python重点知识 钻石_python——子类对象如何访问父类的同名方法

    1. 为什么只说方法不说属性 关于"子类对象如何访问父类的同名属性"是没有意义的.因为父类的属性子类都有,子类还有父类没有的属性,在初始化时,给子类对象具体化所有的给定属性,完全没 ...

  6. C++:钻石继承与虚继承

    QUESTION:什么是钻石继承? ANSWER:假设我们已经有了两个类Father1和Father2,他们都是类GrandFather的子类.现在又有一个新类Son,这个新类通过多继承机制对类Fat ...

  7. 利用python画钻石_python多继承(钻石继承)问题和解决方法简单示例

    本文实例讲述了python多继承(钻石继承)问题和解决方法.分享给大家供大家参考,具体如下: 在菱形多继承中,如果用父类.__init__()来调用父类的初始化方法,最上层会执行两次,所以遇到这种情况 ...

  8. python 钻石继承_python3--object类,继承与派生,super方法,钻石继承问题

    python3--object类,继承与派生,super方法,钻石继承问题 发布时间:2018-04-13 20:38:05编辑:Run阅读(1914) 昨天内容复习 组合:什么有什么的关系(例:老师 ...

  9. day25 python学习 继承,钻石继承 多态

    ---恢复内容开始--- 通过一个列子认识父类和子类中,子类的如何实现对父类默认属性调用,同时拥有自己的属性,如何在子类中调用父类的方法,class Ainmal:country='afdas'def ...

最新文章

  1. Go 学习笔记(51)— Go 标准库之 strings(字符串比较、字符串前后缀、字符串统计、字符串索引、字符串包含、字符串转换、字符串复制、字符串替换、字符串去除、字符串分割和连接)
  2. java模拟使用接口,关于java:模拟一个类与模拟它的接口
  3. 比赛--建金字塔问题--解题报告
  4. 赛程一览 | 2019 上海国际创客大赛
  5. pusher 创建新应用_使用 Laravel-echo-server 构建实时应用
  6. 禅道项目管理_禅道项目管理软件 v12.5.1 开源版
  7. C#中调用SSIS包的问题
  8. 操作系统课程设计 —— 模拟磁盘文件系统实现 (Java)
  9. 怀集天气预报软件测试,【天气】怀集要入夏?这份天气预报告诉你答案!
  10. 这个计算机到底是咋入门的(1.0)胡学友修改版
  11. 全民社会保障月供制度的客观理由
  12. 分享在实际项目中积累的硬件调试经验 - 调试方法,以及常见调试案例
  13. ios 编译ffmpeg 支持 x264 fdk-aac openssl 并裁剪库大小
  14. net:ERR_UPLOAD_FILE_CHANGED in Chrome
  15. 可穿戴设备数据安全及隐私保护概论
  16. 中国环保机械市场现状调研与未来投资方向预测报告2022-2028年
  17. 华为服务器系统进不去,服务器系统进不去
  18. 后端调用阿里云快递API接口--后端部分
  19. Redis高可用与集群
  20. 谈谈微信公众号的交易价值

热门文章

  1. 量子计算机到底神在哪里说明文,“九章”量子计算机到底有多神!
  2. 握手失败_拜托了,看完这篇别再问我什么是TCP三次握手和四次挥手
  3. 最长回文子串-三种DP实现
  4. android通话记录列表apk,android打电话,发短信,获取通讯录、通话记录、短信记录...
  5. hourglass论文_DSSD(1)_论文_arxiv2017
  6. 模板—扩展GCD*2
  7. git仓库如果是私密的,每台电脑上导下来都需要进行ssh授权,所以一个项目不知一个ssh权限...
  8. ThinkPHP的基本操作
  9. python—迭代器
  10. 基础拾遗------委托详解