1. 程式人生 > >super繼承執行原理

super繼承執行原理

參考連結

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')


class C(A):
    def __init__(self):
        print('enter C')
        super(C, self).__init__()
        print('leave C'
) class D(B, C): def __init__(self): print('enter D') super(D, self).__init__() print('leave D') d = D() print(d.__class__.__mro__) # debug模式下,執行的順序 # print('enter D') # super(D, self).__init__() # print('enter B') # super(B, self).__init__() # print('enter C') # super(C, self).__init__()
# print('enter A') # print('leave A') # super(D, self).__init__() # print('leave C') # super(B, self).__init__() # print('leave B') # super(D, self).__init__() # print('leave D') # super: # 不要一說到 super 就想到父類!super 指的是 MRO 表中的下一個類!super 其實幹的是這件事: # def super(cls, inst): # mro = inst.__class__.mro() # Always the most derived class
# return mro[mro.index(cls) + 1] # 兩個引數 cls 和 inst 分別做了兩件事: # 1. inst 負責生成 MRO 的 list # 2. 通過 cls 定位當前 MRO 中的 index,並返回 mro[index + 1] # 解釋: # 在 B 的 __init__() 函式中: # super(B, self).__init__() # # 首先,我們獲取 self.__class__.__mro__,這裡的 self 是 D 的 instance: # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) # # 然後,通過 B 定位 MRO 中的 index,並找到下一個,顯然是 C,於是,呼叫 C 的 __init__ 列印 enter C。 # # 1)在 MRO 中,基類永遠出現在派生類後面,如果有多個基類,基類的相對順序保持不變。 # 2)super 是針對新式類的,如果用舊式類,就應該用類名去呼叫方法。

此時使用的繼承結構是
這裡寫圖片描述

在這個繼承中,只有一個基類A,繼承分支只有一條,但當分支有多條時

class A:
    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")


class C(A):
    def __init__(self):
        print("Enter C")
        super(C, self).__init__()
        print("Leave C")


class W:
    def __init__(self):
        print("Enter W")
        print("Leave W")


class Q(W):
    def __init__(self):
        print("Enter Q")
        super(Q, self).__init__()
        print("Leave Q")


class D(Q):
    def __init__(self):
        print("Enter D")
        super(D, self).__init__()
        print("Leave D")


class E(D, B, C):
    def __init__(self):
        print("Enter E")
        super(E, self).__init__()
        print("Leave E")


E()
print(E.__mro__)

# 輸出:
# Enter E
# Enter D
# Enter Q
# Enter W
# Leave W
# Leave Q
# Leave D
# Leave E
# (<class '__main__.E'>, <class '__main__.D'>, <class '__main__.Q'>, <class '__main__.W'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

#  只進入第一個分支

此時的繼承圖
這裡寫圖片描述

此時繼承的分支結構不是同一個終點,只會選擇其中一條,按照mro順序的開始的那條進行執行繼承

就是所謂的廣度優先

class A(object):
    def __init__(self):
        print("enter A")
        print("leave A")


class B(object):
    def __init__(self):
        print("enter B")
        print("leave B")


class C(A):
    def __init__(self):
        print("enter C")
        super(C, self).__init__()
        print("leave C")


class D(A):
    def __init__(self):
        print("enter D")
        super(D, self).__init__()
        print("leave D")


class E(B, C):
    def __init__(self):
        print("enter E")
        B.__init__(self)
        C.__init__(self)
        print("leave E")


class F(E, D):
    def __init__(self):
        print("enter F")
        E.__init__(self)
        D.__init__(self)
        print("leave F")


f = F()
print(F.__mro__)

# 輸出結果:
# 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
# (<class '__main__.F'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)

這種情況跟第二種類似
這裡寫圖片描述

說明:

用 super 時的輸出是不同的。明顯地,F 的初始化不僅完成了所有的父類的呼叫,而且保證了每一個父類的初始化函式只調用一次。

總結一下:

  1. super 並不是一個函式,是一個類名,形如 super(B, self) 事實上呼叫了 super 類的初始化函式,產生了一個 super 物件;
  2. super 類的初始化函式並沒有做什麼特殊的操作,只是簡單記錄了類型別和具體例項;
  3. super(B, self).func 的呼叫並不是用於呼叫當前類的父類的 func 函式;
  4. Python 的多繼承類是通過 mro 的方式來保證各個父類的函式被逐一呼叫,而且保證每個父類函式只調用一次(如果每個類都使用 super);
  5. 混用 super 類和非繫結的函式是一個危險行為,這可能導致應該呼叫的父類函式沒有呼叫或者一個父類函式被呼叫多次。