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中的实例方法、静态方法、类方法、类变量和实例变量浅析
Apr 26 Python
Python中的高级数据结构详解
Mar 27 Python
python下载文件时显示下载进度的方法
Apr 02 Python
用Python抢过年的火车票附源码
Dec 07 Python
Python生成随机验证码的两种方法
Dec 22 Python
Python编程实现正则删除命令功能
Aug 30 Python
Python编程之gui程序实现简单文件浏览器代码
Dec 08 Python
urllib和BeautifulSoup爬取维基百科的词条简单实例
Jan 17 Python
python Opencv将图片转为字符画
Feb 19 Python
Python实现的括号匹配判断功能示例
Aug 25 Python
python os.path.isfile 的使用误区详解
Nov 29 Python
pycharm 如何查看某一函数源码的快捷键
May 12 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
PHP自动更新新闻DIY
2006/10/09 PHP
计算php页面运行时间的函数介绍
2013/07/01 PHP
PHP中怎样保持SESSION不过期 原理及方案介绍
2013/08/08 PHP
php实现webservice实例
2014/11/06 PHP
阿里云PHP SMS短信服务验证码发送方法
2017/07/11 PHP
ThinkPHP5+UEditor图片上传到阿里云对象存储OSS功能示例
2019/08/05 PHP
jQuery关于导航条背景切换效果实现示例
2013/09/04 Javascript
js 采用delete实现继承示例代码
2014/05/20 Javascript
JavaScript匿名函数与委托使用示例
2014/07/22 Javascript
JS鼠标拖拽实例分析
2015/11/23 Javascript
JS实现向iframe中表单传值的方法
2017/03/24 Javascript
jQuery加密密码到cookie的实现代码
2017/04/18 jQuery
jquery.form.js异步提交表单详解
2017/04/25 jQuery
使用 js 简单的实现 bind、call 、aplly代码实例
2019/09/07 Javascript
小程序采集录音并上传到后台
2019/11/22 Javascript
基于JS+HTML实现弹窗提示是否确认提交功能
2020/06/17 Javascript
Selenium执行JavaScript脚本的方法示例
2020/12/31 Javascript
[53:10]Secret vs Pain 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
Python中用函数作为返回值和实现闭包的教程
2015/04/27 Python
编写Python的web框架中的Model的教程
2015/04/29 Python
利用 Monkey 命令操作屏幕快速滑动
2016/12/07 Python
Python中如何获取类属性的列表
2016/12/26 Python
使用python 和 lint 删除项目无用资源的方法
2017/12/20 Python
python爬虫获取京东手机图片的图文教程
2017/12/29 Python
python3下载抖音视频的完整代码
2019/06/05 Python
python买卖股票的最佳时机(基于贪心/蛮力算法)
2019/07/05 Python
如何在python中实现随机选择
2019/11/02 Python
施华洛世奇中国官网:SWAROVSKI中国
2020/06/16 全球购物
给排水专业应届生求职信
2013/10/12 职场文书
大学生入党思想汇报
2014/01/01 职场文书
英语道歉信范文
2014/01/09 职场文书
便利店投资的创业计划书
2014/01/12 职场文书
酒店节能降耗方案
2014/05/08 职场文书
2015年世界急救日宣传活动方案
2015/05/06 职场文书
2015年学校政教工作总结
2015/07/20 职场文书
完美解决golang go get私有仓库的问题
2021/05/05 Golang