zoukankan      html  css  js  c++  java
  • python cookbook第三版学习笔记十一:类和对象(二)调用父类的方法

    在子类中调用父类的方法,可以下面的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.11python.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.11python.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.11python.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.11python.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]的尾部,因此无法构建一个没有二义性的继承关系。系统报错

    
    
    
    
    
  • 相关阅读:
    2015苏州大学ACM-ICPC集训队选拔赛(2)1004
    2015苏州大学ACM-ICPC集训队选拔赛(2)1002
    Codeforces Round #339 (Div. 2) A
    2015苏州大学ACM-ICPC集训队选拔赛(2) 1001 1003 1010
    HDU计算机学院大学生程序设计竞赛(2015’12)Happy Value
    HDU计算机学院大学生程序设计竞赛(2015’12)The Magic Tower
    HDU计算机学院大学生程序设计竞赛(2015’12)The Country List
    2015苏州大学ACM-ICPC集训队选拔赛(1) 1008
    2015苏州大学ACM-ICPC集训队选拔赛(1) 1007
    在Window Embedded CE(Wince)下使用OpenNETCF进行路由表的开发
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/7190732.html
Copyright © 2011-2022 走看看