• 约定
  • 单继承
  • 多继承
  • super 是个类
  • 多继承中 super 的工作方式
  • 参考资料

约定

在开始之前我们来约定一下本文所使用的 Python 版本。默认用的是 Python 3,也就是说:本文所定义的类都是新式类。如果你用到是 Python 2 的话,记得继承 object:

  1. # 默认, Python 3
  2. class A:
  3. pass
  4.  
  5. # Python 2
  6. class A(object):
  7. pass

Python 3 和 Python 2 的另一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :

  1. # 默认,Python 3
  2. class B(A):
  3. def add(self, x):
  4. super().add(x)
  5.  
  6. # Python 2
  7. class B(A):
  8. def add(self, x):
  9. super(B, self).add(x)

所以,你如果用的是 Python 2 的话,记得将本文的 super() 替换为 suepr(Class, self) 。

如果还有其他不兼容 Python 2 的情况,我会在文中注明的。

单继承

在单继承中 super 就像大家所想的那样,主要是用来调用父类的方法的。

  1. class A:
  2. def __init__(self):
  3. self.n = 2
  4.  
  5. def add(self, m):
  6. print('self is {0} @A.add'.format(self))
  7. self.n += m
  8.  
  9. class B(A):
  10. def __init__(self):
  11. self.n = 3
  12.  
  13. def add(self, m):
  14. print('self is {0} @B.add'.format(self))
  15. super().add(m)
  16. self.n += 3

你觉得执行下面代码后, b.n 的值是多少呢?

  1. b = B()
  2. b.add(2)
  3. print(b.n)

执行结果如下:

  1. self is <__main__.B object at 0x106c49b38> @B.add
  2. self is <__main__.B object at 0x106c49b38> @A.add
  3. 8

这个结果说明了两个问题:

  1. super().add(m) 确实调用了父类 A 的 add 方法。
  2. super().add(m) 调用父类方法 def add(self, m) 时, 此时父类中 self 并不是父类的实例而是子类的实例, 所以b.add(2) 之后的结果是 5 而不是 4 。

不知道这个结果是否和你想到一样呢?下面我们来看一个多继承的例子。

多继承

这次我们再定义一个 class C,一个 class D:

  1. class C(A):
  2. def __init__(self):
  3. self.n = 4
  4.  
  5. def add(self, m):
  6. print('self is {0} @C.add'.format(self))
  7. super().add(m)
  8. self.n += 4
  9.  
  10. class D(B, C):
  11. def __init__(self):
  12. self.n = 5
  13.  
  14. def add(self, m):
  15. print('self is {0} @D.add'.format(self))
  16. super().add(m)
  17. self.n += 5

下面的代码又输出啥呢?

  1. d = D()
  2. d.add(2)
  3. print(d.n)

这次的输出如下:

  1. self is <__main__.D object at 0x10ce10e48> @D.add
  2. self is <__main__.D object at 0x10ce10e48> @B.add
  3. self is <__main__.D object at 0x10ce10e48> @C.add
  4. self is <__main__.D object at 0x10ce10e48> @A.add
  5. 19

你说对了吗?你可能会认为上面代码的输出类似:

  1. self is <__main__.D object at 0x10ce10e48> @D.add
  2. self is <__main__.D object at 0x10ce10e48> @B.add
  3. self is <__main__.D object at 0x10ce10e48> @A.add
  4. 15

为什么会跟预期的不一样呢?下面我们将一起来看看 super 的奥秘。

super 是个类

当我们调用 super() 的时候,实际上是实例化了一个 super 类。你没看错, super 是个类,既不是关键字也不是函数等其他数据结构:

  1. >>> class A: pass
  2. ...
  3. >>> s = super(A)
  4. >>> type(s)
  5. <class 'super'>
  6. >>>

在大多数情况下, super 包含了两个非常重要的信息: 一个 MRO 以及 MRO 中的一个类。当以如下方式调用 super 时:

  1. super(a_type, obj)

MRO 指的是 type(obj) 的 MRO, MRO 中的那个类就是 a_type , 同时 isinstance(obj, a_type) == True 。

当这样调用时:

  1. super(type1, type2)

MRO 指的是 type2 的 MRO, MRO 中的那个类就是 type1 ,同时 issubclass(type2, type1) == True 。

那么, super() 实际上做了啥呢?简单来说就是:提供一个 MRO 以及一个 MRO 中的类 C , super() 将返回一个从 MRO 中 C 之后的类中查找方法的对象。

也就是说,查找方式时不是像常规方法一样从所有的 MRO 类中查找,而是从 MRO 的 tail 中查找。

举个栗子, 有个 MRO:

  1. [A, B, C, D, E, object]

下面的调用:

  1. super(C, A).foo()

super 只会从 C 之后查找,即: 只会在 D 或 E 或 object 中查找 foo 方法。

多继承中 super 的工作方式

再回到前面的

  1. d = D()
  2. d.add(2)
  3. print(d.n)

现在你可能已经有点眉目,为什么输出会是

  1. self is <__main__.D object at 0x10ce10e48> @D.add
  2. self is <__main__.D object at 0x10ce10e48> @B.add
  3. self is <__main__.D object at 0x10ce10e48> @C.add
  4. self is <__main__.D object at 0x10ce10e48> @A.add
  5. 19

了吧 ;)

下面我们来具体分析一下:

  • D 的 MRO 是: [D, B, C, A, object] 。 备注: 可以通过 D.mro() (Python 2 使用 D.__mro__ ) 来查看 D 的 MRO 信息)
  • 详细的代码分析如下:
  1. class A:
  2. def __init__(self):
  3. self.n = 2
  4.  
  5. def add(self, m):
  6. # 第四步
  7. # 来自 D.add 中的 super
  8. # self == d, self.n == d.n == 5
  9. print('self is {0} @A.add'.format(self))
  10. self.n += m
  11. # d.n == 7
  12.  
  13. class B(A):
  14. def __init__(self):
  15. self.n = 3
  16.  
  17. def add(self, m):
  18. # 第二步
  19. # 来自 D.add 中的 super
  20. # self == d, self.n == d.n == 5
  21. print('self is {0} @B.add'.format(self))
  22. # 等价于 suepr(B, self).add(m)
  23. # self 的 MRO 是 [D, B, C, A, object]
  24. # 从 B 之后的 [C, A, object] 中查找 add 方法
  25. super().add(m)
  26.  
  27. # 第六步
  28. # d.n = 11
  29. self.n += 3
  30. # d.n = 14
  31.  
  32. class C(A):
  33. def __init__(self):
  34. self.n = 4
  35.  
  36. def add(self, m):
  37. # 第三步
  38. # 来自 B.add 中的 super
  39. # self == d, self.n == d.n == 5
  40. print('self is {0} @C.add'.format(self))
  41. # 等价于 suepr(C, self).add(m)
  42. # self 的 MRO 是 [D, B, C, A, object]
  43. # 从 C 之后的 [A, object] 中查找 add 方法
  44. super().add(m)
  45.  
  46. # 第五步
  47. # d.n = 7
  48. self.n += 4
  49. # d.n = 11
  50.  
  51. class D(B, C):
  52. def __init__(self):
  53. self.n = 5
  54.  
  55. def add(self, m):
  56. # 第一步
  57. print('self is {0} @D.add'.format(self))
  58. # 等价于 super(D, self).add(m)
  59. # self 的 MRO 是 [D, B, C, A, object]
  60. # 从 D 之后的 [B, C, A, object] 中查找 add 方法
  61. super().add(m)
  62.  
  63. # 第七步
  64. # d.n = 14
  65. self.n += 5
  66. # self.n = 19
  67.  
  68. d = D()
  69. d.add(2)
  70. print(d.n)

调用过程图如下:

  1. D.mro() == [D, B, C, A, object]
  2. d = D()
  3. d.n == 5
  4. d.add(2)
  5.  
  6. class D(B, C): class B(A): class C(A): class A:
  7. def add(self, m): def add(self, m): def add(self, m): def add(self, m):
  8. super().add(m) 1.---> super().add(m) 2.---> super().add(m) 3.---> self.n += m
  9. self.n += 5 <------6. self.n += 3 <----5. self.n += 4 <----4. <--|
  10. (14+5=19) (11+3=14) (7+4=11) (5+2=7)

现在你知道为什么 d.add(2) 后 d.n 的值是 19 了吧 ;

Python中super的应用的更多相关文章

  1. python中super().__init__和类名.__init__的区别

    super().__init__相对于类名.__init__,在单继承上用法基本无差 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次 多继承时 ...

  2. Python中super的用法【转载】

    Python中super的用法[转载] 转载dxk_093812 最后发布于2019-02-17 20:12:18 阅读数 1143  收藏 展开 转载自 Python面向对象中super用法与MRO ...

  3. Python中super函数的用法

    之前看python文档的时候发现许多单继承类也用了super()来申明父类,那么这样做有何意义? 从python官网文档对于super的介绍来看,其作用为返回一个代理对象作为代表调用父类或亲类方法.( ...

  4. python中super()的一些用法

    在看python高级编程这本书的时候,在讲到super的时候,产生了一些疑惑,super在python中的用法跟其他的语言有一些不一样的地方,在网上找了一些资料,发现基本上很少有文章能把我的疑惑讲明白 ...

  5. python中super的理解(转)

    原文地址:https://www.zhihu.com/question/20040039 针对你的问题,答案是可以,并没有区别.但是这题下的回答我感觉都不够好. 要谈论 super,首先我们应该无视 ...

  6. Python中super()的用法

    参考链接:https://www.cnblogs.com/shengulong/p/7892266.html super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是 ...

  7. Python中super详解

    转至:https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html 说到 super, 大家可能觉得很 ...

  8. python中super的使用方法

    说白了,super的使用就是要子类要调用父类的方法,我们就用super,那你要有调用的规范,我们明白这个规范就可以了. 在python2和python3中,调用方法不同,注意就是了.Python3.x ...

  9. python中super的使用

    转自:http://python.jobbole.com/86787/ super() 的入门使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能, ...

随机推荐

  1. R语言:R2OpenBUGS

    R语言:R2OpenBUGS 用这个包调用BUGS model,分别用表格和图形概述inference和convergence,保存估计的结果 as.bugs.array 转换成bugs object ...

  2. vs配置SP++3.0

    最近在研究信号处理的C语言算法,突然发现一个西安交大的师兄之前已经写出来了一个完整的库,同样是研究生三年,差别怎么这样大呢. 先从用轮子开始吧. 一.SP++3.0安装及测试 官网下载地址: http ...

  3. 用canvas绘制验证码

    在通常的登录界面我们都可以看到验证码,验证码的作用是检测是不是人在操作,防止机器等非人操作,防止数据库被轻而易举的攻破. 验证码一般用PHP和java等后端语言编写: 但是在前端,用canva或者SV ...

  4. mysql 复制表数据,表结构的3种方法

    什么时候我们会用到复制表?例如:我现在对一张表进行操作,但是怕误删数据,所以在同一个数据库中建一个表结构一样,表数据也一样的表,以作备份.如果用mysqldump比较麻烦,备份.MYD,.MYI这样的 ...

  5. 京东全链路压测军演系统(ForceBot)架构解密

    摘要:全链路压测是应对电商大促容量规划最有效的手段,如何有效进行容量规划是其中的架构关键问题.京东在全链路压测方面做过多年尝试,本文转载京东商城基础平台技术专家文章,介绍其最新的自动化压测 Force ...

  6. CSS3实战-文字篇

    text-shadow的大作用 多颜色阴影效果,用逗号分隔text-shaodow即可. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tr ...

  7. Visual Leak Detector原理剖析

    认识VLD VLD(Visual Leak Detector)是一款用于Visual C++的开源内存泄漏检测工具,我们只需要在被检测内存泄漏的工程代码里#include “vld.h”就可以开启内存 ...

  8. 【转】C# Graphics类详解

    Brush 类 .NET Framework 4 定义用于填充图形形状(如矩形.椭圆.饼形.多边形和封闭路径)的内部的对象. 属于命名空间:  System.Drawing 这是一个抽象基类,不能进行 ...

  9. 用threading和Queue模块实现多线程的端口扫描器

    一.Queue模块基础 q = Queue.Queue()    q.qsize()           返回队列的大小  q.empty()         如果队列为空,返回True,反之Fals ...

  10. NYOJ 123 士兵杀敌(四) (线段树)

    题目链接 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个 ...