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删除java文件头上版权信息的方法
Jul 31 Python
Python实现从百度API获取天气的方法
Mar 11 Python
简单实现python收发邮件功能
Jan 05 Python
django连接mysql数据库及建表操作实例详解
Dec 10 Python
Python中使用gflags实例及原理解析
Dec 13 Python
使用python求解二次规划的问题
Feb 29 Python
使用Jupyter notebooks上传文件夹或大量数据到服务器
Apr 14 Python
python定时截屏实现
Nov 02 Python
Django解决frame拒绝问题的方法
Dec 18 Python
python爬虫线程池案例详解(梨视频短视频爬取)
Feb 20 Python
Python中快速掌握Data Frame的常用操作
Mar 31 Python
python创建字典及相关管理操作
Apr 13 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
解析ajax事件的调用顺序
2013/06/17 PHP
php绘制一个扇形的方法
2015/01/24 PHP
使用GD库生成带阴影文字的图片
2015/03/27 PHP
超详细的php用户注册页面填写信息完整实例(附源码)
2015/11/17 PHP
PHP递归算法的简单实例
2019/02/28 PHP
JavaScript几种形式的树结构菜单
2010/05/10 Javascript
js预载入和JavaScript Image()对象使用介绍
2011/08/28 Javascript
深入理解JavaScript系列(9) 根本没有“JSON对象”这回事!
2012/01/15 Javascript
浅析js中取绝对值的2种方法
2013/07/09 Javascript
jquery提取元素里的纯文本不包含span等里的内容
2013/09/30 Javascript
js操纵dom生成下拉列表框的方法
2014/02/24 Javascript
js拖拽一些常见的思路方法整理
2014/03/19 Javascript
JS替换字符串中字符即替换全部而不是第一个
2014/06/04 Javascript
node.js中的console.error方法使用说明
2014/12/10 Javascript
关于input全选反选恶心的异常情况
2016/07/24 Javascript
js实现放大镜特效
2017/05/18 Javascript
es6 字符串String的扩展(实例讲解)
2017/08/03 Javascript
React实践之Tree组件的使用方法
2017/09/30 Javascript
基于vue监听滚动事件实现锚点链接平滑滚动的方法
2018/01/17 Javascript
webpack4简单入门实例
2018/09/06 Javascript
vue 解决循环引用组件报错的问题
2018/09/06 Javascript
BootStrap table实现表格行拖拽效果
2018/12/01 Javascript
微信小程序MUI侧滑导航菜单示例(Popup弹出式,左侧滑动,右侧不动)
2019/01/23 Javascript
python利用微信公众号实现报警功能
2018/06/10 Python
使用python读取csv文件快速插入数据库的实例
2018/06/21 Python
python实现桌面壁纸切换功能
2019/01/21 Python
python 获得任意路径下的文件及其根目录的方法
2019/02/16 Python
Python分析彩票记录并预测中奖号码过程详解
2019/07/09 Python
使用Python通过oBIX协议访问Niagara数据的示例
2020/12/04 Python
国家地理在线商店:Shop National Geographic
2018/06/30 全球购物
双立人美国官方商店:ZWILLING集团餐具和炊具
2020/05/07 全球购物
天网工程实施方案
2014/03/26 职场文书
优秀共产党员事迹材料
2014/12/18 职场文书
详解Laravel框架的依赖注入功能
2021/05/27 PHP
CSS font-variation 可变字体的魅力(实例详解)
2022/03/03 HTML / CSS
mysql通过group by分组取最大时间对应数据的两种有效方法
2022/09/23 MySQL