深入解析Python编程中super关键字的用法


Posted in Python onJune 24, 2016

官方文档中关于super的定义说的不是很多,大致意思是返回一个代理对象让你能够调用一些继承过来的方法,查找的机制遵循mro规则,最常用的情况如下面这个例子所示:

class C(B):
  def method(self, arg):
    super(C, self).method(arg)

子类C重写了父类B中同名方法method,在重写的实现中通过super实例化的代理对象调用父类的同名方法。

super类的初始方法签名如下:

def __init__(self, type1, type2=None): # known special case of super.__init__
    """
    super(type, obj) -> bound super object; requires isinstance(obj, type)
    super(type) -> unbound super object
    super(type, type2) -> bound super object; requires issubclass(type2, type)
    Typical use to call a cooperative superclass method:

除去self外接受一个或者或者两个参数,如同注释声明的一样,接受两个参数时返回的是绑定的super实例,省略第二个参数的时候返回的是未绑定的super对象。

一般情况下当调用继承的类方法或者静态方法时,并不需要绑定具体的实例,这个时候使用super(type, type2).some_method就能达到目的,当然super(type, obj)在这种情况下也能够使用,super对象有自定义实现的getattribute方法也能够处理。不过,后者一般用来调用实例方法,这样在查找方法的时候能够传入相应的实例,从而得到绑定的实例方法:

class A(object):
  def __init__(self):
    pass

  @classmethod
  def klass_meth(cls):
    pass

  @staticmethod
  def static_meth():
    pass

  def test(self):
    pass

class B(A):
  pass

>>> b = B()
>>> super(B, b).test
<bound method B.test of <__main__.B object at 0x02DA3570>>
>>> super(B, b).klass_meth
<bound method type.klass_meth of <class '__main__.B'>>
>>> super(B, b).static_meth
<function static_meth at 0x02D9CC70>
>>> super(B, B).test
<unbound method B.test>
>>> super(B, B).klass_meth
<bound method type.klass_meth of <class '__main__.B'>>
>>> super(B,B).satic_meth
>>> super(B,B).static_meth
<function static_meth at 0x02D9CC70>

初始化super对象的时候,传递的第二个参数其实是绑定的对象,第一个参感觉数可以粗暴地理解为标记查找的起点,比如上面例子中的情况:super(B, b).test就会在B.__mro__里面列出的除B本身的类中查找方法test,因为方法都是非数据描述符,在super对象的自定义getattribute里面实际上会转化成A.__dict['test'].__get__(b, B)。

super在很多地方都会用到,除了让程序不必hardcode指定类型让代码更加动态,还有其他一些具体必用的地方比如元类中使用super查找baseclass里面的new生成自定义的类型模板;在自定义getattribute的时候用来防止无限循环等等。

关于super建议读者将它与python的描述符一起来理解,因为super就实现了描述符的协议,是一个非数据描述符,能够帮助大家更好的理解super的使用和工作原理。

同时,有以下4个点值得大家注意:
1、单继承时super()和__init__()实现的功能是类似的

class Base(object):
  def __init__(self):
    print 'Base create'

class childA(Base):
  def __init__(self):
    print 'creat A ',
    Base.__init__(self)


class childB(Base):
  def __init__(self):
    print 'creat B ',
    super(childB, self).__init__()

base = Base()

a = childA()
b = childB()

输出结果:

Base create
creat A Base create
creat B Base create

使用super()继承时不用显式引用基类。

2、super()只能用于新式类中

把基类改为旧式类,即不继承任何基类

class Base():
  def __init__(self):
    print 'Base create'

执行时,在初始化b时就会报错:

super(childB, self).__init__()
TypeError: must be type, not classobj

3、super不是父类,而是继承顺序的下一个类

    在多重继承时会涉及继承顺序,super()相当于返回继承顺序的下一个类,而不是父类,类似于这样的功能:

def super(class_name, self):
  mro = self.__class__.mro()
  return mro[mro.index(class_name) + 1]

    mro()用来获得类的继承顺序。

例如:

class Base(object):
  def __init__(self):
    print 'Base create'

class childA(Base):
  def __init__(self):
    print 'enter A '
    # Base.__init__(self)
    super(childA, self).__init__()
    print 'leave A'


class childB(Base):
  def __init__(self):
    print 'enter B '
    # Base.__init__(self)
    super(childB, self).__init__()
    print 'leave B'

class childC(childA, childB):
  pass

c = childC()
print c.__class__.__mro__

输入结果如下:

enter A 
enter B 
Base create
leave B
leave A
(<class '__main__.childC'>, <class '__main__.childA'>, <class '__main__.childB'>, <class '__main__.Base'>, <type 'object'>)

    supder和父类没有关联,因此执行顺序是A —> B—>—>Base

    执行过程相当于:初始化childC()时,先会去调用childA的构造方法中的 super(childA, self).__init__(), super(childA, self)返回当前类的继承顺序中childA后的一个类childB;然后再执行childB().__init()__,这样顺序执行下去。

    在多重继承里,如果把childA()中的 super(childA, self).__init__() 换成Base.__init__(self),在执行时,继承childA后就会直接跳到Base类里,而略过了childB:

enter A 
Base create
leave A
(<class '__main__.childC'>, <class '__main__.childA'>, <class '__main__.childB'>, <class '__main__.Base'>, <type 'object'>)

 

    从super()方法可以看出,super()的第一个参数可以是继承链中任意一个类的名字,

    如果是本身就会依次继承下一个类;

    如果是继承链里之前的类便会无限递归下去;

    如果是继承链里之后的类便会忽略继承链汇总本身和传入类之间的类;

    比如将childA()中的super改为:super(childC, self).__init__(),程序就会无限递归下去。

    如:

File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
 File "C:/Users/Administrator/Desktop/crawler/learn.py", line 10, in __init__
  super(childC, self).__init__()
RuntimeError: maximum recursion depth exceeded while calling a Python object

4、super()可以避免重复调用

    如果childA基础Base, childB继承childA和Base,如果childB需要调用Base的__init__()方法时,就会导致__init__()被执行两次:

class Base(object):
  def __init__(self):
    print 'Base create'

class childA(Base):
  def __init__(self):
    print 'enter A '
    Base.__init__(self)
    print 'leave A'


class childB(childA, Base):
  def __init__(self):
    childA.__init__(self)
    Base.__init__(self)

b = childB()
  Base的__init__()方法被执行了两次

enter A 
Base create
leave A
Base create
使用super()是可避免重复调用

class Base(object):
  def __init__(self):
    print 'Base create'

class childA(Base):
  def __init__(self):
    print 'enter A '
    super(childA, self).__init__()
    print 'leave A'


class childB(childA, Base):
  def __init__(self):
    super(childB, self).__init__()

b = childB()
print b.__class__.mro()
enter A 
Base create
leave A
[<class '__main__.childB'>, <class '__main__.childA'>, <class '__main__.Base'>, <type 'object'>]
Python 相关文章推荐
haskell实现多线程服务器实例代码
Nov 26 Python
Python cx_freeze打包工具处理问题思路及解决办法
Feb 13 Python
Python下载指定页面上图片的方法
May 12 Python
python万年历实现代码 含运行结果
May 20 Python
Python实现的微信公众号群发图片与文本消息功能实例详解
Jun 30 Python
python读取txt文件,去掉空格计算每行长度的方法
Dec 20 Python
Python2与Python3的区别实例分析
Apr 11 Python
Django ORM 聚合查询和分组查询实现详解
Aug 09 Python
Python3多线程版TCP端口扫描器
Aug 31 Python
tensorflow 保存模型和取出中间权重例子
Jan 24 Python
python GUI库图形界面开发之PyQt5布局控件QGridLayout详细使用方法与实例
Mar 06 Python
python中Ansible模块的Playbook的具体使用
May 28 Python
深入了解Python数据类型之列表
Jun 24 #Python
Python实现信用卡系统(支持购物、转账、存取钱)
Jun 24 #Python
Python提取Linux内核源代码的目录结构实现方法
Jun 24 #Python
Linux上安装Python的PIL和Pillow库处理图片的实例教程
Jun 23 #Python
尝试用最短的Python代码来实现服务器和代理服务器
Jun 23 #Python
Python基础篇之初识Python必看攻略
Jun 23 #Python
浅谈python中scipy.misc.logsumexp函数的运用场景
Jun 23 #Python
You might like
用php制作简单分页(从数据库读取记录)的方法详解
2013/05/04 PHP
PHP利用超级全局变量$_GET来接收表单数据的实例
2016/11/05 PHP
PHP+MySQL实现输入页码跳转到指定页面功能示例
2018/06/01 PHP
thinkphp5框架扩展redis类方法示例
2019/05/06 PHP
php面向对象基础详解【星际争霸游戏案例】
2020/01/23 PHP
PHP 判断字符串是中文还是英文, 或者是中英混合
2021/03/09 PHP
一些javascript一些题目的解析
2010/12/25 Javascript
jquery.cookie() 方法的使用(读取、写入、删除)
2013/12/05 Javascript
jQuery中Dom的基本操作小结
2014/01/23 Javascript
JavaScript+CSS实现仿Mootools竖排弹性动画菜单效果
2015/10/14 Javascript
基于AngularJS实现页面滚动到底自动加载数据的功能
2015/10/16 Javascript
利用CSS3在Angular中实现动画
2016/01/15 Javascript
AngularJS ng-bind-template 指令详解
2016/07/30 Javascript
jquery插件bootstrapValidator数据验证详解
2016/11/09 Javascript
Bootstrap表格使用方法详解
2017/02/17 Javascript
javascript图片预览和上传(兼容IE)
2017/03/15 Javascript
详解在AngularJS的controller外部直接获取$scope
2017/06/02 Javascript
vue-cli webpack 引入swiper的操作方法
2018/09/15 Javascript
原生js实现Flappy Bird小游戏
2018/12/24 Javascript
[01:02:25]2014 DOTA2华西杯精英邀请赛 5 24 iG VS DK
2014/05/26 DOTA
读取json格式为DataFrame(可转为.csv)的实例讲解
2018/06/05 Python
Python通过socketserver处理多个链接
2020/03/18 Python
keras中的History对象用法
2020/06/19 Python
python获取百度热榜链接的实例方法
2020/08/25 Python
CSS3属性box-shadow使用详细教程
2012/01/21 HTML / CSS
Russell Stover巧克力官方网站:美国领先的精美巧克力制造商
2016/11/27 全球购物
伦敦高达60%折扣的钻石珠宝商:Purely Diamonds
2018/06/24 全球购物
在Ajax应用中信息是如何在浏览器和服务器之间传递的
2016/05/31 面试题
博士研究生自我鉴定范文
2013/12/04 职场文书
租房协议书
2014/04/10 职场文书
远程网络教育毕业生自我鉴定
2014/04/14 职场文书
入党函调证明材料
2014/12/24 职场文书
执行力心得体会范文
2016/01/11 职场文书
2019大学生实习报告
2019/06/21 职场文书
汽车销售合同文本
2019/08/08 职场文书
一起来看看Vue的核心原理剖析
2022/03/24 Vue.js