zoukankan      html  css  js  c++  java
  • Python之新式类与经典类

    1、新式类

    在 py3 里面的继承 object 的类,以及它的子类都是新式类

    多继承的情况下,会按照广度优先的方式查找

    为什么调用父类

    class A:   # 默认继承 object类
        def __init__(self):
            print("enter A")
            print("leave A")
    class B(A):
        def __init__(self):
            print("enter B")
            super(B, self).__init__()
            print("leave B")
    
    b = B()
    enter B
    enter A
    leave A
    leave B
    
    通过调用super获得父类实例从而可以实现该实例的初始化函数.  (因为要继承父类的功能, 又要有新的功能).
    View Code

    直接使用父类来调用的差异

    事实上, 上面的super函数方法还可以这么写:

    class A:
        def __init__(self):
            print("enter A")
            print("leave A")
    class B(A):
        def __init__(self):
            print("enter B")
            A.__init__(self)
            print("leave B")
    View Code

    通过直接使用父类类名来调用父类的方法, 实际也是可行的. 起码在上面的例子中效果上他们现在是一样的. 这种方法在老式类中也是唯一的调用父类的方法 (老式类没有super).

    通过父类类名调用方法很常用, 比较直观. 但其效果和super还是有差异的. 例如:

    class A:
        def __init__(self):
            print("enter A")
            print("leave A")
    class B(A):
        def __init__(self):
            print ("enter B")
            A.__init__(self)
            print "leave B")
    class C(A):
        def __init__(self):
            print("enter C")
            A.__init__(self)
            print("leave C")
    class D(B,C):
        def __init__(self):
            print("enter D")
            B.__init__(self)
            C.__init__(self)
            print("leave D")
    >>> d=D()
    enter D
    enter B
    enter A
    leave A
    leave B
    enter C
    enter A
    leave A
    leave C
    leave D
    View Code

    可以发现, 这里面A的初始化函数被执行了两次. 因为我们同时要实现B和C的初始化函数, 所以分开调用两次, 这是必然的结果.

    但如果改写成super呢?

    class A:
        def __init__(self):
            print ("enter A")
            print ("leave A")
    class B(A):
        def __init__(self):
            print ("enter B")
            super().__init__()
            print ("leave B")
    class C(A):
        def __init__(self):
            print ("enter C")
            super().__init__()
            print ("leave C")
    class D(B,C):
        def __init__(self):
            print ("enter D"
            super().__init__()
            print ("leave D"
    >>> d=D()
    enter D
    enter B
    enter C
    enter A
    leave A
    leave C
    leave B
    leave D
    View Code

    会发现所有父类ABC只执行了一次, 并不像之前那样执行了两次A的初始化.

    然后, 又发现一个很奇怪的: 父类的执行是 BCA 的顺序并且是全进入后再统一出去.

    如果没有多继承,super其实和通过父类来调用方法差不多. 但, super还有个好处: 当B继承自A, 写成了A.__init__, 如果根据需要进行重构,继承 class A 全部要改成继承自 E,那么全部都得改一次! 这样很麻烦而且容易出错! 而使用super()就不用一个一个改了(只需子类继承父类,定义中改一改就好了)

    2、MRO表

    MRO是什么? 可以通过以下方式调出来:

    >>> D.mro() # or d.__class__.mro()  or D.__class__.mro(D) 
    [D, B, C, A, object]
    >>> B.mro()
    [B, A, object]
    >>> help(D.mro)
    #Docstring:
    #mro() -> list
    #return a type's method resolution order
    #Type:      method_descriptor
    View Code

    MRO就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表 (类继承顺序表去理解也行) 啦.

    这个表有啥用? 首先了解实际super做了啥:

    def super(cls, inst):
        mro = inst.__class__.mro()
        return mro[mro.index(cls) + 1]
    View Code

    换而言之, super方法实际是调用了cls的在MRO表中的下一个类. 如果是简单一条线的单继承, 那就是父类->父类一个一个地下去罗. 但对于多继承, 就要遵循MRO表中的顺序了. 以上面的D的调用为例:

    d的初始化
    -> D (进入D) super(D,self) 
    -> 父类B (进入B) super(B,self) 
    -> 父类C (进入C) super(C,self) 
    -> 父父类A (进入A)  (退出A) # 如有继续super(A,self)  -> object (停了)
    -> (退出C)
    -> (退出B)
    -> (退出D)
    View Code

    所以, 在MRO表中的超类初始化函数只执行了一次!

    在 MRO 中,基类永远出现在派生类后面,如果有多个基类,基类的相对顺序保持不变。

    这个原则包括两点:

    1.     基类永远在派生类后面
    2.     类定义时的继承顺序影响相对顺序.

    如果有以下继承

    clip_image001

    那么MRO是: F -> E -> B -> C -> D -> A -> object

    • 先找出最长深度最深的继承路线F->E->C->A->object. (因为必然基类永远出现在派生类后面)
    • 类似深度优先, 定出其余顺序: F->E->B->obj, F->D->A-object
    • 如果有多个基类,基类的相对顺序保持不变, 类似于merge时优先提前面的项. 所以排好这些路线: (FEBO, FECAO, FDAO)
    • F->E->B->obj且E(B,C)决定B在C前面.所以F->E->B->C->A->obj (相当于F+merge(EBO,ECAO)).
    • F->D->A-object且F(E,D)决定了D在E后, 所以D在E后A前. 因为相对顺序, 相当于FE+merge(BCAO, DAO), 所以FE BC D AO

    3、经典类

    没有继承 object 的类,以及它的子类都叫经典类

    继承object 的类,以及它的子类都叫新式类

    PY2 是按照深度优先

  • 相关阅读:
    django学习----http协议
    线程进程和协程
    内置函数
    我所了解的递归
    函数的闭包和装饰器
    字符串的内置方法
    格式化输入输出
    逻辑运算符和操作运算符
    python流程控制
    计算机网络原理
  • 原文地址:https://www.cnblogs.com/tootooman/p/9225058.html
Copyright © 2011-2022 走看看