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函数学习笔记
Oct 07 Python
一步步解析Python斗牛游戏的概率
Feb 12 Python
python3+PyQt5实现支持多线程的页面索引器应用程序
Apr 20 Python
Python图像处理之图像的读取、显示与保存操作【测试可用】
Jan 04 Python
Python生成指定数量的优惠码实操内容
Jun 18 Python
50行Python代码获取高考志愿信息的实现方法
Jul 23 Python
将pytorch转成longtensor的简单方法
Feb 18 Python
keras 权重保存和权重载入方式
May 21 Python
Python使用eval函数执行动态标表达式过程详解
Oct 17 Python
Python中pass的作用与使用教程
Nov 13 Python
python 批量将中文名转换为拼音
Feb 07 Python
Python自动化测试PO模型封装过程详解
Jun 22 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模块memcache和memcached区别分析
2011/06/14 PHP
memcache一致性hash的php实现方法
2015/03/05 PHP
深入理解PHP内核(一)
2015/11/10 PHP
PHP的介绍以及优势详细分析
2019/09/05 PHP
jquery 插件 人性化的消息显示
2008/01/21 Javascript
javascript下IE与FF兼容函数收集
2008/09/17 Javascript
Jquery 插件开发笔记整理
2011/01/17 Javascript
jquery图片不完全按比例自动缩小的简单代码
2013/07/29 Javascript
js的hasownproperty使用示例
2014/03/02 Javascript
JavaScript实现数字数组按照倒序排列的方法
2015/04/06 Javascript
jQuery绑定事件监听bind和移除事件监听unbind用法实例详解
2016/01/19 Javascript
jquery对象和DOM对象的任意相互转换
2016/02/21 Javascript
使用postMesssage()实现iframe跨域页面间的信息传递
2016/03/29 Javascript
vue.js中mint-ui框架的使用方法
2017/05/12 Javascript
原生js简单实现放大镜特效
2017/05/16 Javascript
mui上拉加载更多下拉刷新数据的封装过程
2017/11/03 Javascript
webpack中如何使用雪碧图的示例代码
2018/11/11 Javascript
微信小程序页面间值传递的两种方法
2018/11/26 Javascript
JavaScript实现简单贪吃蛇效果
2020/03/09 Javascript
Python字符串详细介绍
2015/05/09 Python
关于Python面向对象编程的知识点总结
2017/02/14 Python
Python创建xml文件示例
2017/03/22 Python
python分布式环境下的限流器的示例
2017/10/26 Python
11月编程语言排行榜 Python逆袭C#上升到第4
2017/11/15 Python
Python爬虫之Spider类用法简单介绍
2020/08/04 Python
谈一谈HTML5本地存储技术
2016/03/02 HTML / CSS
计算机应用专业毕业生求职信
2013/10/24 职场文书
创先争优标语
2014/06/27 职场文书
办理房产证委托书
2014/09/18 职场文书
公司新员工欢迎词
2015/09/30 职场文书
z-index不起作用
2021/03/31 HTML / CSS
go:垃圾回收GC触发条件详解
2021/04/24 Golang
PyQt5爬取12306车票信息程序的实现
2021/05/14 Python
使用Python脚本对GiteePages进行一键部署的使用说明
2021/05/27 Python
《吸血鬼幸存者》新内容发布 追加多个全新模式
2022/04/07 其他游戏
手把手带你彻底卸载MySQL数据库
2022/06/14 MySQL