一、为什么要用super?

在Python 2.2以前,通常的做法:

class A:
def __init__(self):
print "enter A"
print "leave A"
class B(A):
def __init__(self):
print "enter B"
A.__init__(self)
print "leave B"
>>> b = B() enter B
enter A
leave A
leave B

 

问题出现!

使用非绑定的类方法(用类名来引用的方法)来调用,并在参数列表中,引入待绑定的对象(self),从而达到调用父类的目的。

但是,问题来了!当父类发生变化时(如类B的父类由A变为C时),必须遍历整个类定义,把所有的通过非绑定的方法的类名全部替换过来。

class B(C):  # A --> C
def __init__(self):
print "enter B"
C.__init__(self) # A --> C
print "leave B"

问题解决!-引入super

如果代码少,还改得过来,但是一旦代码上万行,就需要慢慢修改了。因此,自Python 2.2开始,Python添加了一个关键字super,来解决这个问题。下面是Python 2.3的官方文档说明:

super(type[, object-or-type])
Return the superclass of type. If the second argument is omitted the super object
returned is unbound. If the second argument is an object, isinstance(obj, type)
must be true. If the second argument is a type, issubclass(type2, type) must be
true. super() only works for new-style classes.
A typical use for calling a cooperative superclass method is:
class C(B):
def meth(self, arg):
super(C, self).meth(arg)
New in version 2.2.

 

所以,上面的代码我们可以改为如下:

class A(object):  # A must be new-style class
def __init__(self):
print "enter A"
print "leave A"
class B(C): # A --> C
def __init__(self):
print "enter B"
super(B, self).__init__()
print "leave B"

 

这样,就只要改继承的类名和基类的object两个地方。 

用super的好处是,可以不用直接引用基类的名称就可以调用基类的方法。如果我们改变了基类的名称,那么所有子类的调用将不用改变。

 

二、新式类和老式类(经典类)区别

从上面可以看出,使用super是从2.2开始,且需要是新式类才支持。新式类和经典类区别如下:

  • 新式类都从object继承,经典类不需要。
  • 新式类的MRO(method resolution order 基类搜索顺序)算法采用C3算法广度优先搜索,而旧式类的MRO算法是采用深度优先搜索。
  • 新式类相同父类只执行一次构造函数,经典类重复执行多次。
  • 新式类的object基类是type类型,经典类的基类是classobj类型。

如下所示:

import inspect

class A:   #改为(object)为新式样类
def __init__(self):
print 'A'
print type(A) class B(A):
def __init__(self):
print 'B'
A.__init__(self) for x in inspect.getmro(B):
print dir(x)
b=B()

  

经典类结果为:

[root@zabbix src]# python tes4.py
['__doc__', '__init__', '__module__']
['__doc__', '__init__', '__module__']
B
A
<type 'classobj'>

  

新式类结果为:

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
B
A
<type 'type'>

三、老式方法(unbound方法)和super方法的差异

 老式方法:

class A(object):
def __init__(self):
print "enter A"
print "leave A" class B(A):
def __init__(self):
print "enter B"
A.__init__(self)
print "leave B" class C(A):
def __init__(self):
print "enter C"
A.__init__(self)
print "leave C" class D(B,C):
def __init__(self):
print "enter D"
B.__init__(self)
C.__init__(self)
print "leave D"

结构如下:

>>> d=D()
enter D
enter B
enter A
leave A
leave B
enter C
enter A
leave A
leave C
leave D

super方法调用:

class A(object):
def __init__(self):
print "enter A"
print "leave A" class B(A):
def __init__(self):
print "enter B"
super(B,self).__init__()
print "leave B" class C(A):
def __init__(self):
print "enter C"
super(C,self).__init__()
print "leave C" class D(B,C):
def __init__(self):
print "enter D"
super(D,self).__init__()
print "leave D"

  

super方法结果如下:

>>> d=D()
enter D
enter B
enter C
enter A
leave A
leave C
leave B
leave D

  

从老式方法的结果可以发现, 这里面A的初始化函数被执行了两次. 因为我们同时要实现B和C的初始化函数, 所以分开调用两次, 这是必然的结果.

从super方法的结果会发现,所有父类ABC只执行了一次, 并不像之前那样执行了两次A的初始化.

然后, 又发现一个很奇怪的: 父类的执行是 BCA 的顺序并且是全进入后再统一出去. 这是MRO表问题

四、The Python 2.3 MRO(Method Resolution Order)

参考文章:https://www.python.org/download/releases/2.3/mro/

1. 如果是经典类MRO为DFS(深度优先搜索(子节点顺序:从下到上,从左到右))。
2. 如果是新式类MRO为BFS(广度优先搜索(子节点顺序:从下到上,从左到右))。

五、总结

1. super并不是一个函数,是一个类名,super(B, self)事实上返回是的一个super对象;

2. super(B, self).func的调用并不是用于调用当前类的父类的func函数;

3. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数只调用一次(如果每个类都使用super,确保没A.func);

4.如果类被设计成使用了super,那么所有子类也必须要调用super,否则直接调用会出现重复调用的问题

5.super不是简单地调用基类的方法,而是调用MRO中的下一个类的方法

python的super函数学习的更多相关文章

  1. python之super()函数

    python之super()函数 python的构造器奇特, 使用魔方. 构造器内对基类对象的初始化同样也很奇特, 奇特到没有半点优雅! 在构造器中使用super(class, instance)返回 ...

  2. 由Python的super()函数想到的

    python-super *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !im ...

  3. Python中super函数的用法

    之前看python文档的时候发现许多单继承类也用了super()来申明父类,那么这样做有何意义? 从python官网文档对于super的介绍来看,其作用为返回一个代理对象作为代表调用父类或亲类方法.( ...

  4. Python关于super()函数的理解

    看下面的例子: class A: def __init__(self, name): self.name = name def bb(self): print('没事就爱瞎BB') class B(A ...

  5. python 中 super函数的使用

    转载地址:http://python.jobbole.com/86787/ 1.简单的使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我 ...

  6. python 高阶函数学习, map、reduce

    一个函数可以接收另一个函数作为参数,这样的函数叫做高阶函数. 函数map(): map()函数接收两个参数,一个是函数,一个是Iterable, map把函数作用于序列的每一个元素,并把结果作为Ite ...

  7. python中format函数学习笔记

    简而言之,format函数就是用{}来代替之前的输出字符时使用的% print('my name is %s and I am %d years old' % ('porsche',23)) 下面详细 ...

  8. python中高阶函数学习笔记

    什么是高阶函数 变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数 def fun(x, y, f): print f(x), f(y) fun ...

  9. python中super函数的参考

    https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ http://wiki.jikexueyuan.com/proj ...

随机推荐

  1. Shell中, 退出整个脚本

    常规做法 cat >test.sh<<EOF'' #!/bin/bash exit_script(){ exit 1 } echo "before exit" e ...

  2. symfony学习笔记1—简介

    1.symfony快速入门还是先看代码结构把,这个是拿到代码的第一印象,app/:整个应用的配置,模版,translations,这个可能是多语言文件什么,src/:项目php文件,vendor/:第 ...

  3. SOJ 4583 动态规划之分组背包

    Description Sidney想去Gandtom家玩.但Sidney家和Gandtom家之间是高低不平.坑坑洼洼的土路.所以他需要用他的背包装几袋稀的泥,在路上铺平一些干的土,使路变成平整的泥土 ...

  4. kubenetes master使用curl 操作API

    前提条件: 已经使用kubeadm 安装集群 查看 kebelet.conf 配置内容 kubectl --kubeconfig /etc/kubernetes/kubelet.conf config ...

  5. docker常用命令(二)

    把镜像保存到本为一个文件 docker save -o filename.tar imagename:tag 载入保存在本地的镜像 docker load < filename.tar 或者 d ...

  6. Spring事务(一)JDBC方式下的事务使用示例

    摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 目录 一.创建数据表结构 二.创建对应数据表的PO 三.创建表与实体间 ...

  7. gulp合并压缩

    1.文件合并压缩 var concat = require(‘gulp-concat’); //引用 var uglify = require(‘gulp-uglify’);  接下来,只要conca ...

  8. shadowssock+openvpn 2.2.2 成功*** -- 好文档 - 原理理解+架构

    [root@iZrj9j4lxhjopzii4vhw0lZ 2.0]# grep -v '^#' vars |grep -v '^$'export EASY_RSA="`pwd`" ...

  9. CanOpen协议【CanFestival】移植方法 支持VC、QT、STM32

    前段时间学习了CanOpen协议,到网上下载的CanFestival3-10源码,移植到VC.QT.STM32等平台,由于网上的资源较少,走了不少弯路,移植好使用过程中才逐渐暴露出各种问题,比如OD字 ...

  10. js实现弹出框的拖拽

    //HTML部分 <div class="wrap"></div> <div class="popUpBox"> <d ...