1. 程式人生 > >python中的MRO和C3演算法

python中的MRO和C3演算法

一. 經典類和新式類

1.python多繼承

  在繼承關係中,python子類自動用友父類中除了私有屬性外的其他所有內容.python支援多繼承.一個類可以擁有多個父類

2.python2和python3的區別

  python2中存在兩種類,一個叫經典類,在python2.2之前,一直使用的經典類.經典類是在基類的根如果什麼都不寫.表示繼承xxx

另一個叫做心事類,在python2.2之後出現了心事類.新式類的特點是基類的根是object

  python3中使用的都是新式類.如果基類誰都不繼承,那這個類會預設繼承object

 

二. 經典類的MRO

  1. 經典類的MRO  樹型結構的深度優先遍歷  -->   樹形結構遍歷

class Foo:
            pass

        class Foo(object):
            pass

        MRO: method resolution order 方法的查詢順序

        class Base:
            pass

        class Base1:
            def chi():
                pass

        class Bar(Base, Base1):
            pass

        b = Bar() # Bar -> Base -> Base1
        b.chi()

  再舉一個例子

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: Foo-> H -> G -> F -> D -> B -> A -> C -> E.

2. 新式類的MRO  C3演算法

C3方法解決順序
讓我介紹幾個簡單的符號,這些符號對以下討論很有用。我將使用快捷方式表示法

C1 C2 ... CN
表示類別列表[C1,C2,...,CN]。

列表的頭部是它的第一個元素:

head = C1
而尾巴是列表的其餘部分:

tail = C2 ... CN。
我也會用這個符號

C +(C1 C2 ... CN)= C C1 C2 ... CN
表示列表的總和[C] + [C1,C2,...,CN]。

現在我可以解釋MRO如何在Python 2.3中執行。

考慮多繼承層次結構中的C類,其中C繼承自基類B1,B2,...,BN。我們想要計算C類的線性化L [C]。規則如下:

C的線性化是C的總和加上父母的線性化和父母的列表的合併。
用符號表示法:

L [C(B1 ... BN)] = C +合併(L [B1] ... L [BN],B1 ... BN)
特別是,如果C是沒有父項的物件類,則線性化是微不足道的:

L [object] =物件。
但是,通常必須根據以下處方計算合併:

取第一個列表的頭部,即L [B1] [0]; 如果這個頭不在任何其他列表的尾部,那麼將它新增到C的線性化並將其從合併中的列表中刪除,否則檢視下一個列表的頭部並將其取出,如果它是好頭。然後重複操作,直到所有課程都被移除或者找不到好頭。在這種情況下,不可能構造合併,Python 2.3將拒絕建立類C並將引發異常。
如果可以保留排序,則此處方確保合併操作保留排序。另一方面,如果無法保留訂單(如上面討論的嚴重訂單不一致的例子),則無法計算合併。

如果C只有一個父(單繼承),那麼合併的計算是微不足道的。在這種情況下

L [C(B)] = C +合併(L [B],B)= C + L [B]

官網解釋 https://www.python.org/download/releases/2.3/mro/

舉例說明

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

把每個類轉換為C3演算法的形式

L(A) = A

L(B) = B + L(A) + A

L(C) = C + L(A) + A

L(D) = D + L(B) + L(C) + BC

L(E) = E + L(C) + L(A) + CA

L(F) = F + L(D) + L(E) + DE

L(G) = G + L(E) + E

L(H) = H + L(G) + L(F) + GF

 之後我們來化簡

加法:merge(), 拿第一項的第一位和 後面每項的除了第一位比較. 如果沒有出現, 則該位元素算出
如果出現了. 此時開始下一項的第一位繼續和後面每一項的除了第一位比較:

L(A) = A

L(B) = B + L(A) + A = BC

L(C) = C + L(A) + A = CA

L(D) = D + L(B) + L(C) + BC = D + BC + CA = DBCA

L(E) = E + L(C) + L(A) + CA = E + CA + A + CA = ECA

L(F) = F + L(D) + L(E) + DE = F + DBCA + ECA = FDBECA

L(G) = G + L(E) + E = G +ECA + E = GECA

L(H) = H + L(G) + L(F) + GF = H + GECA + FDBECA + GF = HGFDBECA

 

三. super

  super() 找MRO順序的下一個

  來看一道面試題

# MRO + super ⾯面試題
class Init(object):
    def __init__(self, v):
        print("init")
        self.val = v
class Add2(Init):
    def __init__(self, val):
        print("Add2")
        super(Add2, self).__init__(val)
        print(self.val)
        self.val += 2
class Mult(Init):
    def __init__(self, val):
        print("Mult")
        super(Mult, self).__init__(val)
        self.val *= 5
class HaHa(Init):
    def __init__(self, val):
        print("哈哈")
        super(HaHa, self).__init__(val)
        self.val /= 5
class Pro(Add2,Mult,HaHa): #
    pass
class Incr(Pro):
    def __init__(self, val):
        super(Incr, self).__init__(val)
        self.val+= 1
# Incr Pro Add2 Mult HaHa Init
p = Incr(5)
print(p.val)
c = Add2(2)
print(c.val)

  

先算出MRO

L(Init) = Init

L(Add2) =Add2 +  L(Init) + Init = Add2 , Init

L(Mult) = Mult + L(Init) + Init = Mult ,Init

L(HaHa) = HaHa + L(Init) + Init = HaHa,Init

L(Pro) = Pro +L(Add2) + L(Mult) + L(HaHa) + Add2,Mult,HaHa = Pro + Add2 , Init + Mult ,Init + HaHa,Init + Add2,Mult,HaHa = Pro,Add2,Mult,HaHa,Init

再進行運算