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 相关文章推荐
在Django的视图中使用数据库查询的方法
Jul 16 Python
使用celery执行Django串行异步任务的方法步骤
Jun 06 Python
Python列表的切片实例讲解
Aug 20 Python
python 实现线程之间的通信示例
Feb 14 Python
Python3交互式shell ipython3安装及使用详解
Jul 11 Python
解决redis与Python交互取出来的是bytes类型的问题
Jul 16 Python
python利用faker库批量生成测试数据
Oct 15 Python
celery在python爬虫中定时操作实例讲解
Nov 27 Python
如何利用python正则表达式匹配版本信息
Dec 09 Python
Python自动化测试基础必备知识点总结
Feb 07 Python
Python快速优雅的批量修改Word文档样式
May 20 Python
python中出现invalid syntax报错的几种原因分析
Feb 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和MySql来与ODBC数据连接
2006/10/09 PHP
PHP+ACCESS 文章管理程序代码
2010/06/21 PHP
snoopy 强大的PHP采集类使用实例代码
2010/12/09 PHP
PHP通过session id 实现session共享和登录验证的代码
2012/06/03 PHP
基于flush()不能按顺序输出时的解决办法
2013/06/29 PHP
PHP中字符安全过滤函数使用小结
2015/02/25 PHP
PHP经典面试题集锦
2015/03/19 PHP
PHP截取IE浏览器并缩小原图的方法
2016/03/04 PHP
php高性能日志系统 seaslog 的安装与使用方法分析
2020/02/29 PHP
JavaScript实际应用:innerHTMl和确认提示的使用
2006/06/22 Javascript
Javascript 刷新全集常用代码
2009/11/22 Javascript
Jquery插件之多图片异步上传
2010/10/20 Javascript
解析js如何获取当前url中的参数值并复制给input
2013/06/23 Javascript
基于jquery插件制作左右按钮与标题文字图片切换效果
2013/11/07 Javascript
jquery等待效果示例
2014/05/01 Javascript
微信JS接口汇总及使用详解
2015/01/09 Javascript
基于javascript实现动态时钟效果
2020/08/18 Javascript
JS+CSS实现下拉刷新/上拉加载插件
2017/03/31 Javascript
JS与jQuery实现子窗口获取父窗口元素值的方法
2017/04/17 jQuery
VeeValidate在vue项目里表单校验应用案例
2018/05/09 Javascript
微信小程序form表单组件示例代码
2018/07/15 Javascript
element-ui中的select下拉列表设置默认值方法
2018/08/24 Javascript
vue输入节流,避免实时请求接口的实例代码
2019/10/30 Javascript
python2.7删除文件夹和删除文件代码实例
2013/12/18 Python
Python常驻任务实现接收外界参数代码解析
2020/07/21 Python
民主生活会对照检查材料
2014/09/22 职场文书
2015年初中生自我评价范文
2015/03/03 职场文书
公司人事任命通知
2015/04/20 职场文书
鲁滨逊漂流记读书笔记
2015/06/26 职场文书
2016年教师师德师风心得体会
2016/01/12 职场文书
多属性、多分类MySQL模式设计
2021/04/05 MySQL
Django drf请求模块源码解析
2021/06/08 Python
mysql如何能有效防止删库跑路
2021/10/05 MySQL
redis数据一致性的实现示例
2022/03/18 Redis
MySQL学习之基础操作总结
2022/03/19 MySQL
怎么禁用Win11输入法 最新Win11输入法关闭教程
2022/08/05 数码科技