深入解析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 相关文章推荐
Python下载指定页面上图片的方法
May 12 Python
Python实现的HMacMD5加密算法示例
Apr 03 Python
python3.6.3+opencv3.3.0实现动态人脸捕获
May 25 Python
使用sklearn进行对数据标准化、归一化以及将数据还原的方法
Jul 11 Python
python将.ppm格式图片转换成.jpg格式文件的方法
Oct 27 Python
解决Python3.5+OpenCV3.2读取图像的问题
Dec 05 Python
pandas每次多Sheet写入文件的方法
Dec 10 Python
浅谈python之高阶函数和匿名函数
Mar 21 Python
在python中实现同行输入/接收多个数据的示例
Jul 20 Python
python实现图像检索的三种(直方图/OpenCV/哈希法)
Aug 08 Python
python导入库的具体方法
Jun 18 Python
Pycharm学生免费专业版安装教程的方法步骤
Sep 24 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实现自动登入google play下载app report的方法
2014/09/23 PHP
PHP中构造函数和析构函数解析
2014/10/10 PHP
php强制更新图片缓存的方法
2015/02/11 PHP
PHP商品秒杀问题解决方案实例详解【mysql与redis】
2019/07/22 PHP
javascript cookie解码函数(兼容ff)
2008/03/17 Javascript
js遍历td tr等html元素
2012/12/13 Javascript
js+css实现的简单易用兼容好的分页
2013/12/30 Javascript
将input框中输入内容显示在相应的div中【三种方法可选】
2017/05/08 Javascript
jQuery+koa2实现简单的Ajax请求的示例
2018/03/06 jQuery
vue-cli脚手架引入图片的几种方法总结
2018/03/13 Javascript
总结javascript三元运算符知识点
2018/09/28 Javascript
基于javascript实现碰撞检测
2020/03/12 Javascript
python实现查找excel里某一列重复数据并且剔除后打印的方法
2015/05/26 Python
Windows中使用wxPython和py2exe开发Python的GUI程序的实例教程
2016/07/11 Python
Python3实现抓取javascript动态生成的html网页功能示例
2017/08/22 Python
Python实现判断一个整数是否为回文数算法示例
2019/03/02 Python
Python实现爬取亚马逊数据并打印出Excel文件操作示例
2019/05/16 Python
Flask框架 CSRF 保护实现方法详解
2019/10/30 Python
python定时任务 sched模块用法实例
2019/11/04 Python
Python稀疏矩阵及参数保存代码实现
2020/04/18 Python
详解css3中 text-fill-color属性
2019/07/08 HTML / CSS
美国最大的珠宝首饰网上商城:Jewelry.com
2016/07/22 全球购物
西班牙英格列斯百货官网:El Corte Inglés
2016/09/25 全球购物
介绍一下SQL注入攻击的种类和防范手段
2012/02/18 面试题
劳动实践课感言
2014/02/01 职场文书
历史专业学生的自我评价
2014/02/28 职场文书
课前三分钟演讲稿
2014/04/24 职场文书
竞选体育委员演讲稿
2014/04/26 职场文书
法制宣传日活动总结
2014/04/29 职场文书
结对共建工作方案
2014/06/02 职场文书
大学迎新标语
2014/06/26 职场文书
房屋维修协议书范本
2014/09/25 职场文书
学困生帮扶工作总结
2015/08/13 职场文书
python实现web邮箱扫描的示例(附源码)
2021/03/30 Python
Python 机器学习工具包SKlearn的安装与使用
2021/05/14 Python
详解MongoDB的条件查询和排序
2021/06/23 MongoDB