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 02 Python
解决Python中list里的中文输出到html模板里的问题
Dec 17 Python
浅谈Python批处理文件夹中的txt文件
Mar 11 Python
详解pandas.DataFrame中删除包涵特定字符串所在的行
Apr 04 Python
python3通过selenium爬虫获取到dj商品的实例代码
Apr 25 Python
Python数据类型之Set集合实例详解
May 07 Python
用Python抢火车票的简单小程序实现解析
Aug 14 Python
Python操作MySQL数据库实例详解【安装、连接、增删改查等】
Jan 17 Python
Python3-异步进程回调函数(callback())介绍
May 02 Python
Python sublime安装及配置过程详解
Jun 29 Python
基于python实现百度语音识别和图灵对话
Nov 02 Python
基于OpenCV的网络实时视频流传输的实现
Nov 15 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缓存类实例
2014/07/18 PHP
PHP常见漏洞攻击分析
2016/02/21 PHP
PHP四种排序算法实现及效率分析【冒泡排序,插入排序,选择排序和快速排序】
2018/04/27 PHP
Yii2框架redis基本应用示例
2018/07/13 PHP
javascript中直接写php代码的方法
2013/07/31 Javascript
js验证电话号码与手机支持+86的正则表达式
2014/01/23 Javascript
js实现身份证号码验证的简单实例
2014/02/19 Javascript
jQuery知识点整理
2015/01/30 Javascript
jQuery之简单的表单验证实例
2016/07/07 Javascript
jquery购物车结算功能实现方法
2020/10/29 Javascript
微信小程序 textarea 组件详解及简单实例
2017/01/10 Javascript
C#微信小程序服务端获取用户解密信息实例代码
2017/03/10 Javascript
Bootstrap面板(Panels)的简单实现代码
2017/03/17 Javascript
AngulaJS路由 ui-router 传参实例
2017/04/28 Javascript
基于 Vue.js 2.0 酷炫自适应背景视频登录页面实现方式
2018/01/17 Javascript
理解 JavaScript EventEmitter
2018/03/29 Javascript
JavaScript工具库之Lodash详解
2019/06/15 Javascript
如何构建 vue-ssr 项目的方法步骤
2020/08/04 Javascript
浅谈JavaScript节流和防抖函数
2020/08/25 Javascript
vue 如何使用递归组件
2020/10/23 Javascript
详解ES6 扩展运算符的使用与注意事项
2020/11/12 Javascript
Python中json格式数据的编码与解码方法详解
2016/07/01 Python
Python实现截取PDF文件中的几页代码实例
2019/03/11 Python
简单了解Python3里的一些新特性
2019/07/13 Python
如何运行带参数的python脚本
2019/11/15 Python
解决Jupyter Notebook使用parser.parse_args出现错误问题
2020/04/20 Python
Python Django form 组件动态从数据库取choices数据实例
2020/05/19 Python
美国内衣第一品牌:Hanes(恒适)
2016/07/29 全球购物
Linux中如何设置Java环境变量(Ubuntu)
2016/07/24 面试题
车间组长岗位职责
2013/12/20 职场文书
优秀辅导员事迹材料
2014/02/16 职场文书
购房协议书范本
2014/04/11 职场文书
网络技术专业求职信
2014/07/13 职场文书
小学德育工作总结2015
2015/05/12 职场文书
楚门的世界观后感
2015/06/03 职场文书
Python采集股票数据并制作可视化柱状图
2022/04/04 Python