Python编程中对super函数的正确理解和用法解析


Posted in Python onJuly 02, 2016

当在子类需要调用父类的方法时,在python2.2之前,直接用类名调用类的方法,即非绑定的类方法,并把自身对象self作参数传进去。

class A(object): 
  def say(self): 
    print 'I am A' 
 
class B(A): 
  def say(self): 
    print 'I am B' 
    A.say(self) 
 
b = B() 
b.say()

输出

I am B
I am A

这样运作挺好,不过有个问题,当父类改了名字时,就要把这些显式调用父类的一个个更正,子类和父类耦合比较高。
于是python2.2后就推出了super()函数来避免硬编码,不用关心父类名叫什么。
使用super()函数,上面的代码可以写成如下。

class B(A): 
  def say(self): 
    print 'I am B' 
    super(B,self).say()

python3.0后,又做了改良,super()函数不用传参数,即上面的那行代码直接super().say()就行了。

需要注意的问题:

  • super只能用在新式类中。
  • super在多重继承有问题,如果子类继承多个父类,那么super调用第一个父类的方法。
  • 不要混用这两种调用父类方法的方案,要么都用非绑定的类方法,要么都用super。不然可能导致没被调用或者被调用多次。

BUT:
不要一说到 super 就想到父类!super 指的是 MRO 中的下一个类!
一说到 super 就想到父类这是初学者很容易犯的一个错误,也是我当年犯的错误。

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

两个参数 cls 和 inst 分别做了两件事:
1. inst 负责生成 MRO 的 list
2. 通过 cls 定位当前 MRO 中的 index, 并返回 mro[index + 1]
这两件事才是 super 的实质,一定要记住!
MRO 全称 Method Resolution Order,它代表了类继承的顺序。

举个例子:

class Root(object):
  def __init__(self):
    print("this is Root")

class B(Root):
  def __init__(self):
    print("enter B")
    # print(self) # this will print <__main__.D object at 0x...>
    super(B, self).__init__()
    print("leave B")

class C(Root):
  def __init__(self):
    print("enter C")
    super(C, self).__init__()
    print("leave C")

class D(B, C):
  pass

d = D()
print(d.__class__.__mro__)

输出

enter B
enter C
this is Root
leave C
leave B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Root'>, <type 'object'>)

知道了 super 和父类其实没有实质关联之后,我们就不难理解为什么 enter B 下一句是 enter C 而不是 this is Root(如果认为 super 代表“调用父类的方法”,会想当然的认为下一句应该是this is Root)。流程如下,在 B 的 __init__ 函数中:

super(B, self).__init__()

首先,我们获取 self.__class__.__mro__,注意这里的 self 是 D 的 instance 而不是 B 的

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Root'>, <type 'object'>)

然后,通过 B 来定位 MRO 中的 index,并找到下一个。显然 B 的下一个是 C。于是,我们调用 C 的 __init__,打出 enter C。

顺便说一句为什么 B 的 __init__ 会被调用:因为 D 没有定义 __init__,所以会在 MRO 中找下一个类,去查看它有没有定义 __init__,也就是去调用 B 的 __init__。

其实这一切逻辑还是很清晰的,关键是理解 super 到底做了什么。

Python 相关文章推荐
Python深入学习之上下文管理器
Aug 31 Python
Python模仿POST提交HTTP数据及使用Cookie值的方法
Nov 10 Python
python处理csv数据的方法
Mar 11 Python
在Docker上开始部署Python应用的教程
Apr 17 Python
python matplotlib中文显示参数设置解析
Dec 15 Python
python 文件转成16进制数组的实例
Jul 09 Python
python 3.6.2 安装配置方法图文教程
Sep 18 Python
Python基于plotly模块实现的画图操作示例
Jan 23 Python
Python使用线程来接收串口数据的示例
Jul 02 Python
18个Python脚本可加速你的编码速度(提示和技巧)
Oct 17 Python
python中的selenium安装的步骤(浏览器自动化测试框架)
Mar 17 Python
Python开发工具Pycharm的安装以及使用步骤总结
Jun 24 Python
Python中的复制操作及copy模块中的浅拷贝与深拷贝方法
Jul 02 #Python
快速排序的算法思想及Python版快速排序的实现示例
Jul 02 #Python
Python使用functools模块中的partial函数生成偏函数
Jul 02 #Python
Python之父谈Python的未来形式
Jul 01 #Python
举例讲解Python的lambda语句声明匿名函数的用法
Jul 01 #Python
Python内置数据结构与操作符的练习题集锦
Jul 01 #Python
Python设置默认编码为utf8的方法
Jul 01 #Python
You might like
BBS(php &amp; mysql)完整版(二)
2006/10/09 PHP
pw的一个放后门的方法分析
2007/10/08 PHP
php木马webshell扫描器代码
2012/01/25 PHP
PHP APC缓存配置、使用详解
2014/03/06 PHP
destoon利用Rewrite规则设置网站安全
2014/06/21 PHP
LINUX下PHP程序实现WORD文件转化为PDF文件的方法
2016/05/13 PHP
Laravel手动返回错误码示例
2019/10/22 PHP
Javascript操纵Cookie实现购物车程序
2006/11/23 Javascript
Javascript string 扩展库代码
2010/04/09 Javascript
过虑特殊字符输入的js代码
2010/08/05 Javascript
JavaScript高级程序设计(第3版)学习笔记3 js简单数据类型
2012/10/11 Javascript
js实现按一下删除键删除整个单词附demo
2014/09/05 Javascript
js实现简单锁屏功能实例
2015/05/27 Javascript
jquery实现网页定位导航
2016/08/23 Javascript
KnockoutJS 3.X API 第四章之click绑定
2016/10/10 Javascript
Bootstrap基本插件学习笔记之Tooltip提示工具(18)
2016/12/08 Javascript
vue.js利用defineProperty实现数据的双向绑定
2017/04/28 Javascript
Angular.JS中select下拉框设置value的方法
2017/06/20 Javascript
VUE axios上传图片到七牛的实例代码
2017/07/28 Javascript
vue移动UI框架滑动加载数据的方法
2018/03/12 Javascript
详解react native页面间传递数据的几种方式
2018/11/07 Javascript
在layui框架中select下拉框监听更改事件的例子
2019/09/20 Javascript
如何在vue中使用kindeditor富文本编辑器
2020/12/19 Vue.js
Python字符串详细介绍
2015/05/09 Python
Pycharm学习教程(4) Python解释器的相关配置
2017/05/03 Python
浅谈Python3中strip()、lstrip()、rstrip()用法详解
2019/04/29 Python
Python多线程thread及模块使用实例
2020/04/28 Python
JAVA SWT事件四种写法实例解析
2020/06/05 Python
CSS3支持IE6, 7, and 8的边框border属性
2012/12/28 HTML / CSS
详解HTML5通讯录获取指定多个人的信息
2016/12/20 HTML / CSS
HTML5 video播放器全屏(fullScreen)方法实例
2015/04/24 HTML / CSS
主要的Ajax框架都有什么
2013/11/14 面试题
2014年公务员退休工资改革方案
2014/10/01 职场文书
交通事故责任认定书
2015/08/06 职场文书
Idea连接MySQL数据库出现中文乱码的问题
2021/04/14 MySQL
选购到合适的激光打印机
2022/04/21 数码科技