1. 程式人生 > >Day-19 面向物件06 多繼承 MRO C3演算法 super()

Day-19 面向物件06 多繼承 MRO C3演算法 super()

一、python多繼承

  1.經典類的MRO

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

class E:
    pass

class F(D, E):
    pass

class G(F, D):
    pass

class H:
    pass

class Foo(H, G):
    pass

  對於這樣的MRO,畫圖即可:

  類的MRO:Foo —> H —> G —> F —> E —> D —> B —> A —> C

  2.新式類的MRO

    python中的新式類的MRO是採用的C3演算法來完成的

class A:
    pass
class B(A):
    pass
class C(A):
    pass
class D(B, C):
    pass
class E(C, A):
    pass
class F(D, E):
    pass
class G(E):
    pass
class H(G, F):
    pass 

  首先. 我們要確定從H開始找. 也就是說. 建立的是H的物件.
  如果從H找. 那找到H+H的父類的C3, 我們設C3演算法是L(x) , 即給出x類. 找到x的MRO
    L(H) = H + L(G) + L(F) +(G,F)
  繼續從程式碼中找G和F的父類往裡面帶
    L(G) = G + L(E) +(E)
    L(F) = F + L(D)+ L(E) + (D,E)
  繼續找E 和 D
    L(E) = E + L(C) + L(A) +(C,A)
    L(D) = D + L(B) + L(C) +(B,C)
  繼續找B和C
    L(B) = B + L(A) +(A)
    L(C) = C + L(A) +(A)

  最後就剩下一個A了. 也就不用再找了. 接下來. 把L(A) 往裡帶. 再推回去. 但要記住. 這裡的+ 表示的是merge. merge的原則是用每個元組的頭一項和後面元組的除頭一項外的其他元素進行比較, 看是否存在. 如果存在. 就從下一個元組的頭一項繼續找. 如果找不到. 就拿出來.

作為merge的結果的一項. 以此類推. 直到元組之間的元素都相同. 也就不用再找了. 

L(B) =(B,) + (A,) +(A,)-> (B, A)
L(C) =(C,) + (A,) +(A,)-> (C, A)
繼續帶.
L(E) = (E,) + (C, A) + (A) + (C,A) -> E, C, A
L(D) = (D,) + (B, A) + (C, A)  +(B,C)-> D, B, A
繼續帶.
L(G) = (G,) + (E, C, A) +(E) -> G, E, C, A
L(F) = (F,) + (D, B, A) + (E, C, A)+ (D,E) -> F, D, B, E, C, A
加油, 最後了
L(H) = (H, ) + (G, E, C, A) + ( F, D, B, E, C, A) +(G,F) -> H, G, F, D, B, E, C, A

算完了. 最終結果 HGFDBECA. 那這個算完了. 如何驗證呢? 其實python早就給你準備好
了. 我們可以使用類名.__mro__獲取到類的MRO資訊.

print(H.__mro__)
結果: 
(<class '__main__.H'>, <class '__main__.G'>, <class '__main__.F'>, <class
'__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class'__main__.C'>,<class '__main__.A'>, <class 'object'>) 

  這個說完了. 那C3到底怎麼看更容易呢? 其實很簡單. C3是把我們多個類產生的共同繼承留到最後去找. 所以. 我們也可以從圖上來看到相關的規律. 這個要大家自己多寫多畫圖就能感覺到了. 但是如果沒有所謂的共同繼承關係. 那幾乎就當成是深度遍歷就可以了.

  

二、super()

  super()可以幫我們執行MRO中下一個父類的方法. 通常super()有兩個使用的地方: 

  1. 可以訪問父類的構造方法

  2. 當子類方法想呼叫父類(MRO)中的方法

  第一種:

 

class Foo:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

class Bar(Foo):
    def __init__(self, a, b, c, d):
        super().__init__(a, b, c) # 訪問⽗類的構造⽅法
        self.d = d

b = Bar(1, 2, 3, 4)
print(b.__dict__)

結果: 
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

 

  這樣就方便了子類. 不需要寫那麼多了. 直接用父類的構造幫我們完成一部分程式碼

  第二種:

class Foo:
    def func1(self):
        super().func1() # 此時找的是MRO順序中下一個類的func1()方法
        print("我的老家. 就住在這個屯")

class Bar:
    def func1(self):
        print("你的老家. 不在這個屯")

class Ku(Foo, Bar):
    def func1(self):
        super().func1() # 此時super找的是Foo
        print("他的老家. 不知道在哪個屯")

k = Ku() # 先看MRO . KU, FOO, BAR object
k.func1()

k2 = Foo() # 此時的MRO. Foo object
k2.func1() # 報錯