Python深入:super函数
新式类中最酷的,或者也是最不平常的特性之一,可能就是编写“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函数的更多相关文章
- python之super()函数
python之super()函数 python的构造器奇特, 使用魔方. 构造器内对基类对象的初始化同样也很奇特, 奇特到没有半点优雅! 在构造器中使用super(class, instance)返回 ...
- 由Python的super()函数想到的
python-super *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !im ...
- Python中super函数的用法
之前看python文档的时候发现许多单继承类也用了super()来申明父类,那么这样做有何意义? 从python官网文档对于super的介绍来看,其作用为返回一个代理对象作为代表调用父类或亲类方法.( ...
- python的super函数学习
一.为什么要用super? 在Python 2.2以前,通常的做法: class A: def __init__(self): print "enter A" print &quo ...
- Python关于super()函数的理解
看下面的例子: class A: def __init__(self, name): self.name = name def bb(self): print('没事就爱瞎BB') class B(A ...
- python 中 super函数的使用
转载地址:http://python.jobbole.com/86787/ 1.简单的使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我 ...
- python中super函数的参考
https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ http://wiki.jikexueyuan.com/proj ...
- super函数
Python面向对象中super用法与MRO机制:https://www.cnblogs.com/chenhuabin/p/10058594.html python 中 super函数的使用:http ...
- Python内置函数(30)——super
英文文档: super([type[, object-or-type]]) Return a proxy object that delegates method calls to a parent ...
- Python内置函数(63)——super
英文文档: super([type[, object-or-type]]) Return a proxy object that delegates method calls to a parent ...
随机推荐
- 全栈数据工程师养成攻略:Python 基本语法
全栈数据工程师养成攻略:Python 基本语法 Python简单易学,但又博大精深.许多人号称精通Python,却不会写Pythonic的代码,对很多常用包的使用也并不熟悉.学海无涯,我们先来了解一些 ...
- webpack4配置基础
前言 为什么要使用构建工具? 1.转换ES6语法(很多老版本的浏览器不支持新语法) 2.转换JSX 3.CSS前缀补全/预处理器 4.压缩混淆(将代码逻辑尽可能地隐藏起来) 5.图片压缩 6. ...
- linux追加中文字库,解决imagemagick 中文乱码的问题。
Windows下的字体丰富多样,而且显示的工整.漂亮. 所以自己想把windows上的字体移到Ubuntu下来.Windows下字体库的位置为C:\Windows\fonts,这里面包含所有windo ...
- shell linux基本命令实例、笔记
1. 在当前文件夹下.查找20分钟内,被訪问过的文件, 并将文件的详情显示出来: find ./ -name '*.log' -mmin -20 -exec ls -l {} \; 当然,须要指出 ...
- GitHub and Git
book:https://git-scm.com/book/zh/v2 Git使用简易指南:https://www.bootcss.com/p/git-guide
- ML面试1000题系列(71-80)
本文总结ML面试常见的问题集 转载来源:https://blog.csdn.net/v_july_v/article/details/78121924 71.看你是搞视觉的,熟悉哪些CV框架,顺带聊聊 ...
- arcgis增大缩放级别
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- cmd操作oracle
利用cmd的操作命令导出,详情如下(备注:方法二是转载网上的教程):1:G:\Oracle\product\10.1.0\Client_1\NETWORK\ADMIN目录下有个tnsname.ora文 ...
- 分析ajax请求过程以及请求方法
ajax 的全称是Asynchronous JavaScript and XML,其中,Asynchronous 是异步的意思,它有别于传统web开发中采用的同步的方式.据小编翻墙了解到,ajax很早 ...
- 计算机网络3.2&3.3(第二节介质&第三节多路复用)
有限的传播介质 双绞线 双绞线电缆 双绞线总结 2 同轴电缆 粗细电缆的传输距离 现在基本都用双绞线和光线 同轴电缆用于居民小区和家庭使用 3 光纤 光纤中以光信号的形式进行传播 正如我们现在看到这样 ...