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实现图片批量剪切示例
Mar 25 Python
Python下singleton模式的实现方法
Jul 16 Python
用Python的Django框架完成视频处理任务的教程
Apr 02 Python
Python实现模拟登录及表单提交的方法
Jul 25 Python
解决nohup重定向python输出到文件不成功的问题
May 11 Python
python 读取.csv文件数据到数组(矩阵)的实例讲解
Jun 14 Python
pyqt5 删除layout中的所有widget方法
Jun 25 Python
Django中提示消息messages的设置方式
Nov 15 Python
Python3 ID3决策树判断申请贷款是否成功的实现代码
May 21 Python
如何解决cmd运行python提示不是内部命令
Jul 01 Python
python 如何读、写、解析CSV文件
Mar 03 Python
pytorch 中autograd.grad()函数的用法说明
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调整服务器时间的方法
2015/04/03 PHP
为你总结一些php信息函数
2015/10/21 PHP
PHP文件操作之获取目录下文件与计算相对路径的方法
2016/01/08 PHP
封装好的省市地区联动控件附下载
2007/08/13 Javascript
基于jQuery的Spin Button自定义文本框数值自增或自减
2010/07/17 Javascript
javascript的parseFloat()方法精度问题探讨
2013/11/26 Javascript
Firefox下无法正常显示年份的解决方法
2014/09/04 Javascript
JavaScript函数使用的基本教程
2015/06/04 Javascript
jquery+CSS3实现淘宝移动网页菜单效果
2015/08/31 Javascript
JS闭包与延迟求值用法示例
2016/12/22 Javascript
vue vuex vue-rouert后台项目——权限路由(适合初学)
2017/12/29 Javascript
js构建二叉树进行数值数组的去重与优化详解
2018/03/26 Javascript
React 使用browserHistory项目访问404问题解决
2018/06/01 Javascript
node.js 使用 net 模块模拟 websocket 握手进行数据传递操作示例
2020/02/11 Javascript
[43:47]DOTA2上海特级锦标赛主赛事日 - 4 败者组第四轮#2 MVP.Phx VS Fnatic第一局
2016/03/05 DOTA
Android分包MultiDex策略详解
2017/10/30 Python
django admin 后台实现三级联动的示例代码
2018/06/22 Python
简单谈谈python基本数据类型
2018/09/26 Python
浅谈Python反射 &amp; 单例模式
2019/03/21 Python
浅析python中while循环和for循环
2019/11/19 Python
PyInstaller运行原理及常用操作详解
2020/06/13 Python
Pytorch实验常用代码段汇总
2020/11/19 Python
家乐福巴西网上超市:Carrefour巴西
2016/10/31 全球购物
澳大利亚小众服装品牌:Maurie & Eve
2018/03/27 全球购物
运动会广播稿400字
2014/01/25 职场文书
计算机学生求职信范文
2014/01/30 职场文书
项目投资建议书
2014/05/16 职场文书
家长会标语
2014/06/24 职场文书
幼儿园个人师德总结
2015/02/06 职场文书
2015年教师党员承诺书
2015/04/27 职场文书
红色故事汇观后感
2015/06/18 职场文书
宿舍卫生管理制度
2015/08/05 职场文书
遗嘱范文
2015/08/07 职场文书
DSP接收机前端设想
2022/04/05 无线电
尝试使用Python爬取城市租房信息
2022/04/12 Python
详细介绍MySQL中limit和offset的用法
2022/05/06 MySQL