新式类中最酷的,或者也是最不平常的特性之一,可能就是编写“cooperative类”。‘cooperative类’通过多继承,使用我称之为‘cooperative super call’的模式。

先来看一下传统的,非cooperative super call的特点:当类C继承了基类B时,C希望覆盖定义在B中的一个方法m,当在C中定义的方法m中,调用了B中定义的m方法时,一个super call就产生了。比如:

class B:
def m(self):
print "B here" class C(B):
def m(self):
print "C here"
B.m(self)

这时,我们称C的方法m扩展了B的方法m。这种模式在单继承中工作的很好,但是在多继承中就未必了。我们来看一下下面的4个类,他们的继承关系形成了一个菱形图。

class A(object): ..

class B(A): ...

class C(A): ...

class D(B, C): ...

假设A定义了方法m,然后B和C都扩展了它。那么D怎么办呢?它继承了m的两个实现,一个来自B,一个来自C。传统上,python简单的使用它最早发现的那个。这种情况下,B中的m就是第一个被检索到的。

但是这并不理想,因为这完全忽视了C中的定义。看一下忽视C中m的错误在哪,假设这些类代表了某种类型的持续性容器。并且,考虑一个方法实现了‘将数据存储到磁盘’的操作。大概情况下,D的实例拥有B和C的数据,以及A的数据。那忽略C中该方法的定义,意味着D的实例仅仅会保存A和B的数据,而不会保存C中定义的数据。

在C++中,D继承了两个相互冲突的m的定义,那会引发一个错误信息。然后D的作者就会重写m来避免这种错误。但是D中的m会做什么呢?它会调用C的m,然后是B的m。但是它们又都继承自A。所以A的m会被调用两次。经典的python会有同样的问题,除了它根本不认为继承两个冲突定义的方法是错误的:它简单的选择第一个。

传统的解决方法是:将m分为两部分,一部分是_m,它仅保存自己的数据,另一部分是m,它会调用自己的_m以及父类的_m们。比如:

class A(object):
def m(self): "save A's data" class B(A):
def _m(self): "save B's data"
def m(self): self._m(); A.m(self) class C(A):
def _m(self): "save C's data"
def m(self): self._m(); A.m(self) class D(B, C):
def _m(self): "save D's data"
def m(self): self._m();B._m(self); C._m(self); A.m(self)

这种模式有多个问题,首先是冗余的方法和定义,但是更重要的是:它引起了继承类对父类不必要的依赖:A的存在不仅是B和C要考虑的实现细节,甚至D也需要知道。如果未来我们需要移除A和B,C之间依赖关系,这也会影响到D。类似的,如果我们为B和C增加一个父类AA,那么B和C的子类也必须知道这一点。

‘call-next-method’的模式,结合新的MRO,很好的解决了这个问题,如下:

class A(object):
def m(self): "save A's data" class B(A):
def m(self): "save B's data"; super(B, self).m() class C(A):
def m(self): "save C's data"; super(C, self).m() class D(B, C):
def m(self): "save D's data"; super(D, self).m()

注意:这里super的第一个参数永远是调用它的类;第二个参数总是self。现在,为了解释super是如何工作的,考虑每个类的MRO,可以查看每个类的__mro__属性:

A.__mro__ == (A, object)

B.__mro__ == (B, A, object)

C.__mro__ == (C, A, object)

D.__mro__ == (D, B, C, A, object)

表达式super(C,self).m应该仅用在类C的m的实现中。要牢记在心的是:self是C的实例,但是self.__class__不一定就是C;他可能是C的子类,比如D。

表达式super(C,self).m会查询self.__class__.__mro__中,C的位置,然后,在这个位置之后,开始寻找m方法的实现。比如,如果self是C的实例,那么super(C,self).m将会找到A中的m,同样的,super(B,self).m中,如果self是B的实例,也会有同样的效果。

但是考虑D的实例,在D中的m方法中,super(D,self).m将会搜索,然后找到了B.m(self),因为B是在D.__mro__中紧跟在D之后的,第一个实现了m的类。然后,在B.m中,调用了super(B,self).m()。因为self是D的实例,而MRO是(D,B,C,A,object),并且,跟在B之后的是C,所以就会调用C.m。在C.m中,调用super(C, self).m(),依然是寻找相同的MRO,可以看到跟在C后的是A,因此A.m被调用了,而这就是终点了。

要注意的是:同样的super表达式,却找到了不同的实现了m的类,这取决于self。这是cooperativesuper 机制的关键所在。

下面是一个super内嵌类的完整实现:

class Super(object):
def __init__(self, type, obj=None):
self.__type__ = type
self.__obj__ = obj def __get__(self, obj, type=None):
if self.__obj__ is None and obj is not None:
return Super(self.__type__, obj)
else:
return self def __getattr__(self, attr):
if isinstance(self.__obj__, self.__type__):
starttype = self.__obj__.__class__
else:
starttype = self.__obj__ mro = iter(starttype.__mro__) for cls in mro:
if cls is self.__type__:
break # Note: mro is an iterator, so the second loop
# picks up where the first one left off!
for cls in mro:
if attr in cls.__dict__:
x = cls.__dict__[attr]
if hasattr(x,"__get__"):
x = x.__get__(self.__obj__)
return x raise AttributeError, attr class A(object):
def m(self):
return "A" class B(A):
def m(self):
return "B" + Super(B, self).m() class C(A):
def m(self):
return "C" + Super(C, self).m() class D(C, B):
def m(self):
return "D" + Super(D, self).m() print D().m() # "DCBA"

https://www.python.org/download/releases/2.2.3/descrintro/

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函数学习

    一.为什么要用super? 在Python 2.2以前,通常的做法: class A: def __init__(self): print "enter A" print &quo ...

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

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

  6. python 中 super函数的使用

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

  7. python中super函数的参考

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

  8. super函数

    Python面向对象中super用法与MRO机制:https://www.cnblogs.com/chenhuabin/p/10058594.html python 中 super函数的使用:http ...

  9. Python内置函数(30)——super

    英文文档: super([type[, object-or-type]]) Return a proxy object that delegates method calls to a parent ...

  10. Python内置函数(63)——super

    英文文档: super([type[, object-or-type]]) Return a proxy object that delegates method calls to a parent ...

随机推荐

  1. 全栈数据工程师养成攻略:Python 基本语法

    全栈数据工程师养成攻略:Python 基本语法 Python简单易学,但又博大精深.许多人号称精通Python,却不会写Pythonic的代码,对很多常用包的使用也并不熟悉.学海无涯,我们先来了解一些 ...

  2. webpack4配置基础

    前言 为什么要使用构建工具? 1.转换ES6语法(很多老版本的浏览器不支持新语法) 2.转换JSX  3.CSS前缀补全/预处理器  4.压缩混淆(将代码逻辑尽可能地隐藏起来)  5.图片压缩  6. ...

  3. linux追加中文字库,解决imagemagick 中文乱码的问题。

    Windows下的字体丰富多样,而且显示的工整.漂亮. 所以自己想把windows上的字体移到Ubuntu下来.Windows下字体库的位置为C:\Windows\fonts,这里面包含所有windo ...

  4. shell linux基本命令实例、笔记

    1. 在当前文件夹下.查找20分钟内,被訪问过的文件, 并将文件的详情显示出来: find ./ -name '*.log' -mmin -20 -exec ls -l {} \;   当然,须要指出 ...

  5. GitHub and Git

    book:https://git-scm.com/book/zh/v2 Git使用简易指南:https://www.bootcss.com/p/git-guide

  6. ML面试1000题系列(71-80)

    本文总结ML面试常见的问题集 转载来源:https://blog.csdn.net/v_july_v/article/details/78121924 71.看你是搞视觉的,熟悉哪些CV框架,顺带聊聊 ...

  7. arcgis增大缩放级别

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  8. cmd操作oracle

    利用cmd的操作命令导出,详情如下(备注:方法二是转载网上的教程):1:G:\Oracle\product\10.1.0\Client_1\NETWORK\ADMIN目录下有个tnsname.ora文 ...

  9. 分析ajax请求过程以及请求方法

    ajax 的全称是Asynchronous JavaScript and XML,其中,Asynchronous 是异步的意思,它有别于传统web开发中采用的同步的方式.据小编翻墙了解到,ajax很早 ...

  10. 计算机网络3.2&3.3(第二节介质&第三节多路复用)

    有限的传播介质 双绞线 双绞线电缆 双绞线总结 2 同轴电缆 粗细电缆的传输距离 现在基本都用双绞线和光线 同轴电缆用于居民小区和家庭使用 3 光纤 光纤中以光信号的形式进行传播 正如我们现在看到这样 ...