Python中super()函数简介及用法分享


Posted in Python onJuly 11, 2016

首先看一下super()函数的定义:

super([type [,object-or-type]])

Return a **proxy object** that delegates method calls to a **parent or sibling** class of type.

返回一个代理对象, 这个对象负责将方法调用分配给第一个参数的一个父类或者同辈的类去完成.

parent or sibling class 如何确定?

第一个参数的__mro__属性决定了搜索的顺序, super指的的是 MRO(Method Resolution Order) 中的下一个类, 而不一定是父类!

super()和getattr() 都使用__mro__属性来解析搜索顺序, __mro__实际上是一个只读的元组.

MRO中类的顺序是怎么排的呢?

实际上MRO列表本身是根据一种C3的线性化处理技术确定的, 理论说明可以参考这里, 这里只简单说明一下原则:

在MRO中, 基类永远出现在派生类的后面, 如果有多个基类, 基类的相对顺序不变.

MRO实际上是对继承树做层序遍历的结果, 把一棵带有结构的树变成了一个线性的表, 所以沿着这个列表一直往上, 就可以无重复的遍历完整棵树, 也就解决了多继承中的Diamond问题.

比如说:

class Root:
  pass

class A(Root):
  pass

class B(Root):
  pass

class C(A, B):
  pass

print(C.__mro__)

# 输出结果为:
# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Root'>, <class 'object'>)

super()实际返回的是一个代理的super对象!

调用super()这个构造方法时, 只是返回一个super()对象, 并不做其他的操作.

然后对这个super对象进行方法调用时, 发生的事情如下:

找到第一个参数的__mro__列表中的下一个直接定义了该方法的类, 并实例化出一个对象
然后将这个对象的self变量绑定到第二个参数上, 返回这个对象

举个例子:

class Root:
  def __init__(self):
    print('Root')

class A(Root):
  def __init__(self):
    super().__init__() # 等同于super(A, self).__init__()

在A的构造方法中, 先调用super()得到一个super对象, 然后向这个对象调用init方法, 这是super对象会搜索A的__mro__列表, 找到第一个定义了__init__方法的类, 于是就找到了Root, 然后调用Root.__init__(self), 这里的self是super()的第二个参数, 是编译器自动填充的, 也就是A的__init__的第一个参数, 这样就完成对__init__方法调用的分配.

注意: 在许多语言的继承中, 子类必须调用父类的构造方法, 就是为了保证子类的对象能够填充上父类的属性! 而不是初始化一个父类对象...(我之前就一直是这么理解的..). Python中就好多了, 所谓的调用父类构造方法, 就是明明白白地把self传给父类的构造方法, 我的小身子骨就这么交给你了, 随便你怎么折腾吧:joy:

参数说明

super() -> same as super(__class__, <first argument>) # <first argument>指的是调用super的函数的第一个参数
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)

 Typical use to call a cooperative superclass method:
  class C(B):
    def meth(self, arg):
      super().meth(arg)
  This works for class methods too:
  class C(B):
    @classmethod
    def cmeth(cls, arg):
      super().cmeth(arg)

如果提供了第二个参数, 则找到的父类对象的self就绑定到这个参数上, 后面调用这个对象的方法时, 可以自动地隐式传递self.
如果第二个参数是一个对象, 则isinstance(obj, type)必须为True. 如果第二个参数为一个类型, 则issubclass(type2, type)必须为True

如果没有传递第二个参数, 那么返回的对象就是Unbound, 调用这个unbound对象的方法时需要手动传递第一个参数, 类似于Base.__int__(self, a, b).

不带参数的super()只能用在类定义中(因为依赖于caller的第二个参数), 编译器会自动根据当前定义的类填充参数.
也就是说, 后面所有调用super返回对象的方法时, 第一个参数self都是super()的第二个参数. 因为Python中所谓的方法, 就是一个第一个参数为self的函数, 一般在调用方法的时候a.b()会隐式的将a赋给b()的第一个参数.

super()的两种常见用法:

单继承中, super用来指代隐式指代父类, 避免直接使用父类的名字
多继承中, 解决Diamond问题 (TODO)

对面向对象的理解

其实我觉得Python里面这样的语法更容易理解面向对象的本质, 比Java中隐式地传this更容易理解.

所谓函数, 就是一段代码, 接受输入, 返回输出. 所谓方法, 就是一个函数有了一个隐式传递的参数. 所以方法就是一段代码, 是类的所有实例共享的, 唯一不同的是各个实例调用的时候传给方法的this 或者self不一样而已.

构造方法是什么呢? 其实也是一个实例方法啊, 它只有在对象生成了之后才能调用, 所以Python中__init__方法的参数是self啊. 调用构造方法时其实已经为对象分配了内存, 构造方法只是起到初始化的作用, 也就是为这段内存里面赋点初值而已.

Java中所谓的静态变量其实也就是类的变量, 其实也就是为类也分配了内存, 里面存了这些变量, 所以Python中的类对象我觉得是很合理的, 也比Java要直观. 至于静态方法, 那就与对象一点关系都没有了, 本质就是个独立的函数, 只不过写在了类里面而已. 而Python中的classmethod其实也是一种静态方法, 不过它会依赖于cls对象, 这个cls就是类对象, 但是只要想用这个方法, 类对象必然是存在的, 不像实例对象一样需要手动的实例化, 所以classmethod也可以看做是一种静态变量. 而staticmethod就是真正的静态方法了, 是独立的函数, 不依赖任何对象.

Java中的实例方法是必须依赖于对象存在的, 因为要隐式的传输this, 如果对象不存在这个this也没法隐式了. 所以在静态方法中是没有this指针的, 也就没法调用实例方法. 而Python中的实例方法是可以通过类名来调用的, 只不过因为这时候self没办法隐式传递, 所以必须得显式地传递.

Python 相关文章推荐
python检测远程端口是否打开的方法
Mar 14 Python
探究python中open函数的使用
Mar 01 Python
Python编程实现微信企业号文本消息推送功能示例
Aug 21 Python
Python绘制七段数码管实例代码
Dec 20 Python
用Python写一段用户登录的程序代码
Apr 22 Python
python matplotlib绘图,修改坐标轴刻度为文字的实例
May 25 Python
使用python生成杨辉三角形的示例代码
Aug 29 Python
Pycharm 文件更改目录后,执行路径未更新的解决方法
Jul 19 Python
django ModelForm修改显示缩略图 imagefield类型的实例
Jul 28 Python
基于Python的图像数据增强Data Augmentation解析
Aug 13 Python
Python控制台输出时刷新当前行内容而不是输出新行的实现
Feb 21 Python
Python爬虫逆向分析某云音乐加密参数的实例分析
Dec 04 Python
Swift中的协议(protocol)学习教程
Jul 08 #Python
Python中多线程的创建及基本调用方法
Jul 08 #Python
使用PyInstaller将Python程序文件转换为可执行程序文件
Jul 08 #Python
Python遍历目录中的所有文件的方法
Jul 08 #Python
Python常用的内置序列结构(列表、元组、字典)学习笔记
Jul 08 #Python
使用Python读写及压缩和解压缩文件的示例
Jul 08 #Python
Python中的FTP通信模块ftplib的用法整理
Jul 08 #Python
You might like
php cout&amp;lt;&amp;lt;的一点看法
2010/01/24 PHP
解决PHP4.0 和 PHP5.0类构造函数的兼容问题
2013/08/01 PHP
ThinkPHP3.2.3数据库设置新特性
2015/03/05 PHP
php实现用于计算执行时间的类实例
2015/04/18 PHP
Laravel实现构造函数自动依赖注入的方法
2016/03/16 PHP
PHP测试框架PHPUnit组织测试操作示例
2018/05/28 PHP
PHP 实现手机端APP支付宝支付功能
2018/06/07 PHP
PHP基于curl实现模拟微信浏览器打开微信链接的方法示例
2019/02/15 PHP
jquery 学习笔记 传智博客佟老师附详细注释
2020/09/12 Javascript
Jquery Ajax 学习实例2 向页面发出请求 返回JSon格式数据
2010/03/15 Javascript
关于div自适应高度/左右高度自适应一致的js代码
2013/03/22 Javascript
Query中click(),bind(),live(),delegate()的区别
2013/11/19 Javascript
JS实现的左侧竖向滑动菜单效果代码
2015/10/19 Javascript
JS+CSS实现的竖向简洁折叠菜单效果代码
2015/10/22 Javascript
vue系列之动态路由详解【原创】
2017/09/10 Javascript
深入理解Node.js中通用基础设计模式
2017/09/19 Javascript
基于Vue中点击组件外关闭组件的实现方法
2018/03/06 Javascript
bootstrap动态调用select下拉框的实例代码
2018/08/09 Javascript
详解Vue中组件的缓存
2019/04/20 Javascript
python中使用百度音乐搜索的api下载指定歌曲的lrc歌词
2014/07/18 Python
pycharm下打开、执行并调试scrapy爬虫程序的方法
2017/11/29 Python
浅谈python 里面的单下划线与双下划线的区别
2017/12/01 Python
Python内置模块hashlib、hmac与uuid用法分析
2018/02/12 Python
Python API 自动化实战详解(纯代码)
2019/06/11 Python
css3 矩阵的使用详解
2018/03/20 HTML / CSS
Zavvi西班牙:电子游戏、极客服装、Blu-ray、Funko Pop等
2019/05/03 全球购物
俄罗斯茶和咖啡网上商店:Tea.ru
2021/01/26 全球购物
毕业生如何写自我鉴定
2014/03/15 职场文书
开展创先争优活动总结
2014/08/28 职场文书
在教室放鞭炮的检讨书
2014/09/28 职场文书
2014年基层党支部工作总结
2014/12/04 职场文书
司机岗位职责
2015/02/04 职场文书
大一学生个人总结
2015/02/15 职场文书
原料仓管员岗位职责
2015/04/01 职场文书
2015年董事长秘书工作总结
2015/07/23 职场文书
JavaScript+HTML实现学生信息管理系统
2021/04/20 Javascript