在子类中调用父类的方法,可以下面的A.spam(self)的方法。
class A(object):
    def spam(self):
        print 'A.spam'
class
B(A):
    def spam(self):
        print 'B.spam'
       
A.spam(self)
if __name__=='__main__':
    b=B()
    b.spam()
但是上面的代码有一个问题,如果B的父类变更了,而且有很多子类的父类都不是A了,从A变成C。那么一个个的代码工作量太大了。因此从Python2.2之后采用了super的方法。
class B(C):   #相比之前的代码,只需要改动这一行代码就可以了
    def spam(self):
        print 'B.spam'
       
super(B, self).spam()
针对前面的A.spam()用法.我们再来看一个例子
class A(Base):
    def __init__(self):
        Base.__init__(self)
        print 'A.__init__'
class
B(Base):
    def __init__(self):
        Base.__init__(self)
        print 'B.__init__'
class
C(A,B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print 'C.__init__' if __name__=='__main__':
    c=C()
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__
可以看到Base.__init__被调用了两次。我们将代码改写下
class Base(object):
    def __init__(self):
        print 'Base.__init__'
class
A(Base):
    def __init__(self):
        super(A, self).__init__()
        print 'A.__init__'
class
B(Base):
    def __init__(self):
        super(B, self).__init__()
        print 'B.__init__'
class
C(A,B):
    def __init__(self):
        super(C, self).__init__()
        print 'C.__init__'
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py
Base.__init__
B.__init__
A.__init__
C.__init__
改成super之后,Base.__init__只被调用了一次。原因是在调用super()的时候会根据MRO列表来进行调用。Python会在MRO列表上从左到右开始查找基类。直到找到第一个匹配这个属性的类为止。通过查看__mro__可以得到MRO列表
print C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <type 'object'>)
那么什么是MRO表以及MRO表的工作原理是什么呢
MRO:Method Resolution Order. 作用在于对当前类和基类进行搜索并且确定顺序。在python中有两种类:经典类和新式类。不同之处在于新式类继承于object
先来看下经典类:
class Father():
    def show_infor(self):
        print 'Father.show_infor' class Child1(Father):
    pass class Child2(Father):
    def show_infor(self):
        print 'Child2.show_infor' class GrandChildren(Child1,Child2):
    pass if __name__=='__main__':
    g=GrandChildren()
    g.show_infor()
运行结果如下:
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py
Father.show_infor
GrandChildren继承于Child1和Child2,Child1和Child2继承于Father。Child2以及Father都有实现show_infor的函数。但是最终只有Father的函数运行了。而Child2的并没有运行。
来看下MRO的顺序呢:
print inspect.getmro(GrandChildren)
(<class __main__.GrandChildren at 0x0166F228>, <class __main__.Child1 at 0x0164EEA0>, <class __main__.Father at 0x0164EC70>, <class __main__.Child2 at 0x0166FD88>)
顺序为GrandChildren->Child1->Father->Child2.
这个类的继承关系以及调用关系参考下图。其实真实的继承关系是GrandChildren->Child1->Father->Child2->Father.只保留重复类的第一个结果就是GrandChildren->Child1->Father->Child2. 但是这种继承关系。导致子类的Child2中的show_infor无法调用。因此这是经典类带来的问题

为了解决经典类的这个问题,就引入了新式类。还是之前的代码。唯一需要改动的是class Father(object) 在这里Father继承于object。运行结果是什么样子的呢?
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py
Child2.show_infor
(<class '__main__.GrandChildren'>, <class '__main__.Child1'>, <class '__main__.Child2'>, <class '__main__.Father'>, <type 'object'>)
可以看到结果调用的是Child2的show_infor。最终的调用顺序为GrandChildren->Child1->Child2->Father->object.
在新式类中的调用顺序如下图。但是新式类和经典类不同的是,重复类只保留最后一个。也就是初始的调用顺序为
GrandChildren->Child1->Father->object->Child2->Father->object
由于只保留最后一个重复类,因此最终调用顺序是GrandChildren->Child1->Child2->Father->object.

新式类的调用可以使得Child2的函数得到调用。
那么新式类有问题么,也是有的。我们来看个例子:
Class x(object):pass
Class y(object):pass
Class a(x,y):pass
Class b(y,x):pass
Class c(a,b):pass
这个例子的访问顺序是c->a->x->object->y->object->b->y->object->x->object
只保留重复元素的最后一个结果为c->a->b->y->x->object.这是C的顺序
我们来看下C父类a,b的顺序:
A:a->x->y->object
B:b->y->x->object
可以看到c和a的x,y继承关系是反的,也就是说C作为子类改变了父类a的顺序。在python中子类不能改变基类的方式。如果执行如上的代码,也会报错误。
为此python2.3后采用了C3的方法。
由于C3涉及图论的方法,这里只做简要的介绍:
首先来看个列表L[C]=[c1,c2,c3..cn].其中C1为头,后面的c2,c3,cn都是尾部。C3的搜索顺序遵循如下原则:

1    检查第一个列表的头元素(如 L[B1] 的头),记作 H。

2 若 H 未出现在其它列表的尾部,则将其输出,并将其从所有列表中删除,然后回到步骤1;否则,取出下一个列表的头部记作 H,继续该步骤。

3重复上述步骤,直至列表为空或者不能再找出可以输出的元素。如果是前一种情况,则算法结束;如果是后一种情况,说明无法构建继承关系,Python 会抛出异常。

比如A的计算方式如下:

L[A] = [A] + merge(L[X], L[Y], [X], [Y])           第一步

= [A] + merge([X, object], [Y, object], [X], [Y])   第二步

= [A, X] + merge([object], [Y, object], [Y])    第三步

= [A, X, Y] + merge([object], [object])    第四步

= [A, X, Y, object]

在第三步中,由于object在[Y,object]的尾部,因此跳过此列表得到Y,并将Y从其他列表中删除。最后的结果为A,X,Y,object。对于B也是同样的过程

再来看下C的

L[C] = [C] + merge(L[A], L[B], [A], [B])

= [C] + merge([A, X, Y, object], [B, Y, X, object], [A], [B])  1

= [C, A] + merge([X, Y, object], [B, Y, X, object], [B])       2

= [C, A, B] + merge([X, Y, object], [Y, X, object])            3

在第二步中,由于X存在于[B, Y, X, object]的尾部,而Y存在于merge([X, Y, object]的尾部,因此无法构建一个没有二义性的继承关系。系统报错


												

python cookbook第三版学习笔记十一:类和对象(二)调用父类的方法的更多相关文章

  1. python cookbook第三版学习笔记十:类和对象(一)

    类和对象: 我们经常会对打印一个对象来得到对象的某些信息. class pair:     def __init__(self,x,y):         self.x=x         self. ...

  2. python cookbook第三版学习笔记六:迭代器与生成器

    假如我们有一个列表 items=[1,2,3].我们要遍历这个列表我们会用下面的方式 For i in items:   Print i 首先介绍几个概念:容器,可迭代对象,迭代器 容器是一种存储数据 ...

  3. python cookbook第三版学习笔记十三:类和对象(三)描述器

    __get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...

  4. python cookbook第三版学习笔记十三:类和对象(四)描述器

    __get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...

  5. python cookbook第三版学习笔记十四:类和对象(五)代理类以及内存回收

    代理类: 代理类的作用其实有继承有些类似,如果你想将某个实例的属性访问代理到内部另外一个实例中去,可以用继承也可以用代理.来看下代理的应用: class A:     def spam(self,x) ...

  6. python cookbook第三版学习笔记二十一:利用装饰器强制函数上的类型检查

    在演示实际代码前,先说明我们的目标:能对函数参数类型进行断言,类似下面这样: @typeassert(int, int) ... def add(x, y): ...     return x + y ...

  7. python cookbook第三版学习笔记 一

    数据结构 假设有M个元素的列表,需要从中分解出N个对象,N<M,这会导致分解的值过多的异常.如下: record=['zhf','zhf@163.com','775-555-1212','847 ...

  8. python cookbook第三版学习笔记二十:可自定义属性的装饰器

    在开始本节之前,首先介绍下偏函数partial.首先借助help来看下partial的定义 首先来说下第一行解释的意思: partial 一共有三个部分: (1)第一部分也就是第一个参数,是一个函数, ...

  9. python cookbook第三版学习笔记十六:抽象基类

    假设一个工程中有多个类,每个类都通过__init__来初始化参数.但是可能有很多高度重复且样式相同的__init__.为了减少代码.我们可以将初始化数据结构的步骤归纳到一个单独的__init__函数中 ...

随机推荐

  1. python正则表达式从路径中取文件名出来不加后缀(txt)

    正则表达式[^\\/:*?"<>|\r\n]+$ ---->取文件名包括后缀 e.g. >>>D:\PyCharm 2018.2.4\pythonWork ...

  2. SecureCRT鼠标快速复制粘贴

  3. C语言实现的水仙花数

    #include <stdio.h>void main(){ int ge,shi,bai;      for (int i =100; i < 1000; i++)     {   ...

  4. many-to-one多对一属性说明

    通过many-to-one元素,可以定义一种常见的与另一个持久化类的关联. 这种关系模型是多对一关联(实际上是一个对象引用-译注):这个表的一个外键引用目标表的 主键字段. <many-to-o ...

  5. 最小二乘法及C语言实现

    我们以最简单的一元线性模型来解释最小二乘法.什么是一元线性模型呢? 监督学习中,如果预测的变量是离散的,我们称其为分类(如决策树,支持向量机等),如果预测的变量是连续的,我们称其为回归.回归分析中,如 ...

  6. 优化算法——拟牛顿法之L-BFGS算法

    一.BFGS算法 在"优化算法--拟牛顿法之BFGS算法"中,我们得到了BFGS算法的校正公式: 利用Sherman-Morrison公式可对上式进行变换,得到 令,则得到: 二. ...

  7. RMQ(区间求最值)

    1. 概述 RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A.回答若干询问RMQ(A,i,j)(i,j<=n).返回数列A ...

  8. IP反查网站,ip反查接口,旁站查询接口大全,通过IP查域名汇总:

    http://cn.bing.com/search?q=ip%3A220.181.111.85     http://dns.aizhan.com/?q=www.baidu.com     http: ...

  9. zoj3329--One Person Game(概率dp第六弹:形成环的dp,带入系数,高斯消元)

    One Person Game Time Limit: 1 Second      Memory Limit: 32768 KB      Special Judge There is a very ...

  10. Vue.js 很好,但会比 Angular 或 React 更好吗?

    文章转自:http://www.oschina.net/translate/vuejs-is-good-but-is-it-better-than-angular-or-rea Vue.js 是一个用 ...