zoukankan      html  css  js  c++  java
  • python继承 super()

    写这篇博文,始于以下问题的探究:

     1 #coding:utf-8
     2 class A(object):
     3     def __init__(self):
     4         print 'enter A'
     5         print 'leave A'
     6 class B(object):
     7     def __init__(self):
     8         print 'enter B'
     9         print 'leave B'
    10 
    11 class C(A):
    12     def __init__(self):
    13         print 'enter C'
    14         super(C, self).__init__()
    15         print 'leave C'
    16 
    17 class D(A):
    18     def __init__(self):
    19         print 'enter D'
    20         super(D, self).__init__()
    21         print 'leave D'
    22 
    23 class E(B, C):
    24     def __init__(self):
    25         print 'enter E'
    26         B.__init__(self)
    27         C.__init__(self)
    28         print 'leave E'
    29 
    30 class F(E, D):
    31     def __init__(self):
    32         print 'enter F'
    33         E.__init__(self)
    34         D.__init__(self)
    35         print 'leave F'
    36 f = F()
    使用super()和通过指定父类方法混用

    在上述代码中,类C、D使用super()函数,类E、F通过直接指定父类方法的方式。在此,继承关系用图表示如下:

    预想的输出应该是:

    enter F
    enter E
    enter B
    leave B
    enter C
    enter A
    leave A
    leave C
    leave E
    enter D
    enter A
    leave A
    leave D
    leave F
    预想输出

    实际的输出是:

    enter F
    enter E
    enter B
    leave B
    enter C
    enter D
    enter A
    leave A
    leave D
    leave C
    leave E
    enter D
    enter A
    leave A
    leave D
    leave F
    实际输出

    又一次自以为是的想错了~~

    查阅资料可知,super的调用顺序是使用C3算法得出的。下面讲下C3算法的规则:

    C3算法的核心在merge列表,merge中存放mro(method resolution order)列表,经过某种规则得到最终的调用顺序列表,而初始调用顺序列表中只存放自身类。规则如下:

    在merge列表中,如果第一个mro列表的第一个类是出现在其它mro列表中,并且也是第一个或者不出现其它mro列表,那么这个类就会从这些mro列表中删除,并添加到调用顺序列表中。

    比如以下示例的调用顺序规则演示如下:

    class A(O):pass
    class B(O):pass
    class C(O):pass
    class D(A,B):pass
    class E(C,D):pass
    示例
    mro(A) = [A, O]
    mro(B) = [B, O]
    mro(C) = [C, O]
    mro(D) = [D] + merge(mro(A), mro(B), [A, B])
    = [D] + merge([A, O], [B, O], [A, B])
    = [D, A] + merge([O], [B, O], [B])
    = [D, A, B] + merge([O], [O])
    = [D, A, B, O]
    mro(E) = [E] + merge(mro(C), mro(D), [C, D])
    = [E] + merge([C, O], [D, A, B, O], [C, D])
    = [E, C] + merge([O], [D, A, B, O], [D])
    = [E, C, D] + merge([O], [A, B, O])
    = [E, C, D, A, B] + merge([O], [O])
    = [E, C, D, A, B, O]

    一目了然了吧,这样就可计算出调用顺序了,这个序列存储在MRO只读列表中。比如类E是多重继承,在E中使用super访问某个函数,访问的是哪个类呢?首先在E类继承类中查找,也就是调用顺序表中E后的类C,类C也没有的话就接着查找类D(在这里记着都是查找类本身是否定义了该函数,而不要混淆继承来的函数),就这样按照访问顺序列表一直往后查找,直到找到调用的函数。求解访问顺序的规则有没有更简便的方式呢?有的。理解这么一句话:C3算法是从左到右深度遍历一条路径到它和另一条路径的交叉点前,再深度遍历另一条路径最后遍历交叉点。在这个例子中多重继承的图解如下,根据图去理解就很容易得出C3的访问顺序规则
    [E, C, D, A, B, O]




    上面介绍了继承类中函数访问顺序规则,这样最开始的例子预想出错是因为super()和指定类方法调用的混用。考虑下如果都换为super()调用,是不是就对了呢?是的。实际上不只是super()调用顺序按照C3算法规则,其它普通函数调用也都是这样一层一层往下查找的。
    问题1:为什么不直接使用指定类方法来调用,而使用super()呢?问题2:
    为什么要采用C3算法的访问顺序呢?
    如果多重继承类过多,那么使用指定类来调用方法显得多余繁琐,需要指明类并且假如某一子类的父类发生了变化,由C变成了D,需要遍历该子类,把所有的原先的父类C换为D,很麻烦。所以引入super()不用考虑指明它的上一层是哪个父类。
    基于问题2,我们看如下两个例子,比较不同:
     1 class A():
     2     def foo1(self):
     3         print "A"
     4 class B(A):
     5     def foo2(self):
     6         pass
     7 class C(A):
     8     def foo1(self):
     9         print "C"
    10 class D(B, C):
    11     pass
    12 
    13 d = D()
    14 d.foo1()
    View Code

    输出为:A

     1 class A(object):
     2     def foo1(self):
     3         print "A"
     4 class B(A):
     5     def foo2(self):
     6         pass
     7 class C(A):
     8     def foo1(self):
     9         print "C"
    10 class D(B, C):
    11     pass
    12 
    13 d = D()
    14 d.foo1()
    View Code

    输出为:C

    很容易看出,后者的基类是继承了object类。这样就引出了旧式类和新式类的差别,旧式类是在python2.2以前常用的,基类没有继承object类,在多重继承时调用函数会出现问题。它采用的访问顺序是从左到右的深度遍历,拿上面那个例子来说,D本身没有重写foo1(),则先看B中有没有foo1(),没有则看B继承的A类中有没有,有则调用,绕过了C类中重新的foo1()。

    而新式类,基类都继承object类,在访问顺序上采用C3算法,例子中访问顺序列表为[D,B,C,A],先看B中有没有foo1(),没有则看B的上后一个类C,有的话调用。

  • 相关阅读:
    datalist的用法
    SQL级联删除——删除主表同时删除从表——同时删除具有主外键关系的表
    js
    回调机制
    JS原型链
    多线程请求乌云链接
    Python高频技巧总结[基础篇]
    批量文本读取URL获取正常访问且保留对应IP
    Django基础之视图
    Django框架简介
  • 原文地址:https://www.cnblogs.com/hithink/p/6387661.html
Copyright © 2011-2022 走看看