python存在多重繼承機制,但是先說:盡量不要用多重繼承。
有點多,慢慢看。。。
目錄:
1. 講多重繼承前,先看看:每一代都是單繼承的繼承問題
2. 子類訪問父類 —— super方法
3. 多重繼承 --- 非super
4. 多重繼承 --- super
5. MRO順序 --- C3算法
# -------------------------------------------------------------------------------------
- 講多重繼承前,先看看:每一代都是單繼承的繼承問題
class grandfather(object): ''' grandfather類的定義如下''' con_flag = 1 #普通靜態字段 __con_data = '爺爺' #私有靜態變量只能在類內使用 def __init__(self,name,age,id_): self.name = name self.age = age self.__id = id_ print('grandfather類初始化完成') def __get_age(self): #私有函數只能在類內使用 return self.age def show_age(self): print(self.age) print('grandfather年齡顯示完畢') class father(grandfather): ''' father類的定義如下''' con_flag = 2 #普通靜態字段 __con_data = '爸爸' #私有靜態變量只能在類內使用 class son(father): ''' son類的定義如下''' con_flag = 4 #普通靜態字段 __con_data = '兒子' #私有靜態變量只能在類內使用 if __name__ == '__main__': son1 = son('Tom',10,'001') #實例化時,會依次查找上層父類的__init__函數,注意這個函數並不一定要有 son1.show_age() #子類中有該方法,則會直接執行;如果沒有才去上層父類,每次單繼承時這個很清晰

可見:
(1)son類繼承自father類,而father類繼承自grandfather類;所以son的實例對象也擁有了grandfather類的方法和屬性;只不過屬性/方法名稱相同時,子類的屬性/方法會覆蓋父類。
(2)當對象調用某方法時,先在子類(本類)中查找,找不到再向上查找父類,一直向上直到找到第一個該方法出現的類。
- 子類訪問父類 —— super方法
有時子類中重寫了某些方法或者屬性,但是又想要使用父類的方法或者屬性;或者子類單純的想使用父類的方法或屬性,可以用super方法。讓我們稍微改變一下上述代碼:
class grandfather(object): ''' grandfather類的定義如下''' con_flag = 1 #普通靜態字段 __con_data = '爺爺' #私有靜態變量只能在類內使用 def __init__(self,name,age,id_): self.name = name self.age = age self.__id = id_ print('grandfather類初始化完成') def __get_age(self): #私有函數只能在類內使用 return self.age def show_age(self): print(self.age) print('grandfather年齡顯示完畢') class father(grandfather): ''' father類的定義如下''' con_flag = 2 #普通靜態字段 __con_data = '爸爸' #私有靜態變量只能在類內使用 class son(father): ''' son類的定義如下''' con_flag = 4 #普通靜態字段 __con_data = '兒子' #私有靜態變量只能在類內使用 def show_age(self): super(son, self).show_age() # 顯式表示調用某個類的超類,此時father類並沒有show_age方法,所以繼續向上查找超類,找到grandfather的方法 super().show_age() # super().show_age() 默認指的是當前類son的超類,用於隱式指代父類,而不用提供父類名稱 grandfather.show_age(self) #此時必須提供self參數
print(super().con_flag) #父類father中有該屬性,於是不會繼續向上查找grandfather類了 print('son年齡顯示完畢')
if __name__ == '__main__': son1 = son('Tom',10,'001') son1.show_age() #子類中有該方法,則會直接執行;如果沒有才去上層,每次單繼承時這個很清晰 print(son1.con_flag)

可見:
(1)子類中調用父類(多層回溯的父類)方法或屬性的方式有兩種:
第一種是 super().func_name() 或者 super().attr_name:例如super().show_age() 、super().con_flag;super(son, self).show_age() 這種形式可以顯示指示是調用哪個類的超類 —— 可以確保多重繼承時父類的方法只被執行一次。
第二種是 父類名.父類方法(self) 或者 父類名.父類屬性:注意此時方法中self參數是必須的 —— 會導致多重繼承時父類中的方法被多次執行,所以多重繼承時最好用super方式,但是最好不要用多重繼承。
(2)搜索方式是:單繼承比較簡單,簡言之就是直接依次向上層父類搜索;這個用C3算法(MRO Method Resolution Order)也可以算,后面介紹該算法。
(3)super方法意義:
單繼承:可以不需要父類名稱就可以調用父類方法;因為父類的名稱可能會變化或者調用其他父類。
多重繼承:用於確保各父類只被搜索、調用一次。
(4)super原理:
super(class_name, instance),它所做的事情是:
首先,獲取instance的MRO順序表:instance.__class__.mro(),例如上面的 son1.__class__.mro();
其次,查找class_name在當前MRO列表中的index,然后在instance的MRO列表上搜索class_name類的下一個類。
總結就是:super(class_name, instance)用於在 instance 的 MRO 列表上搜索 class_name類 的下一個類。
- 多重繼承 --- 非super
class grandfather(object): ''' grandfather類的定義如下''' con_flag = 1 #普通靜態字段 __con_data = '爺爺' #私有靜態變量只能在類內使用 def __init__(self,name,age,id_): self.name = name self.age = age self.__id = id_ print('grandfather類初始化完成') def show_age(self): print(self.age) print('grandfather年齡顯示完畢') class father(grandfather): ''' father類的定義如下''' con_flag = 2 #普通靜態字段 __con_data = '爸爸' #私有靜態變量只能在類內使用 def show_age(self): grandfather.show_age(self) #調用父類的方法 print('father年齡顯示完畢') class aunt(grandfather): ''' aunt類的定義如下''' con_flag = 3 #普通靜態字段 __con_data = '姑姑' #私有靜態變量只能在類內使用 def show_age(self): grandfather.show_age(self) #調用父類的方法 print('aunt年齡顯示完畢') class son(father,aunt): ''' son類的定義如下''' con_flag = 4 #普通靜態字段 __con_data = '兒子' #私有靜態變量只能在類內使用 def show_age(self): father.show_age(self) #子類調用父類的方法 aunt.show_age(self) #子類調用父類的方法 print('son年齡顯示完畢') if __name__ == '__main__': son1 = son('Tom',10,'001') son1.show_age()

可見:
(1)子類son調用父類father和aunt的時候,調用了2次基類grandfather的方法(有兩次:grandfather年齡顯示完畢);如果中間的繼承關系更復雜,那么會顯得更難以理解。
(2)這里的調用父類方法的方式是上述第二種,即:父類名.父類方法(self),而不是super那一種。
正因為如此,所以super方法的使用在多重繼承里面更有意義。
- 多重繼承 --- super
class grandfather(object): ''' grandfather類的定義如下''' con_flag = 1 #普通靜態字段 __con_data = '爺爺' #私有靜態變量只能在類內使用 def __init__(self,name,age,id_): self.name = name self.age = age self.__id = id_ print('grandfather類初始化完成') def show_age(self): print(self.age) print('grandfather年齡顯示完畢') return self.con_flag class father(grandfather): ''' father類的定義如下''' con_flag = 2 #普通靜態字段 __con_data = '爸爸' #私有靜態變量只能在類內使用 def show_age(self): s1 = super().show_age() print('father年齡顯示完畢') return s1 class aunt(grandfather): ''' aunt類的定義如下''' con_flag = 3 #普通靜態字段 __con_data = '姑姑' #私有靜態變量只能在類內使用 def show_age(self): s2 = super().show_age() print('aunt年齡顯示完畢') return s2 class son(father,aunt): ''' son類的定義如下''' __con_data = '兒子' #私有靜態變量只能在類內使用 def show_age(self): ss = super().show_age() print('son年齡顯示完畢') print(ss) if __name__ == '__main__': son1 = son('Tom',10,'001') son1.show_age() #子類中有該方法,則會直接執行;如果沒有才去下一個類查找 print(son1._son__con_data) #本身有這個參數,所以不用回溯查找 print(son1.__class__.mro()) #查看MRO順序

可見:
(1)用super時,基類grandfather類只訪問了一次。
(2)相同屬性名稱時,下層屬性名(方法)會覆蓋下一個類的屬性名(或方法),例如con_flag屬性,通過MRO順序,son1對象獲取的是father類的該屬性con_flag;例如__con_data屬性,在本類(son類)中存在,所以會覆蓋下一個類的屬性。
(3)類的搜索順序是:[<class '__main__.son'>, <class '__main__.father'>, <class '__main__.aunt'>, <class '__main__.grandfather'>, <class 'object'>],這是通過C3算法計算出來的MRO順序列表。
(4)之所以說下一個類而不說父類,是因為這個順序是C3算法計算的,不是嚴格的繼承順序。
(5)由MRO順序,解析一下son實例化對象son1所包含的信息:

(6)依據son1對象的信息,分析son1.show_age()的執行過程:

- MRO順序 --- C3算法
python官網上有詳細解釋,這里稍微展開一下:
1. 首先 (C1C2C3...Cn)表示一個多重繼承序列,注意這個順序很重要;
2. 則 head(頭)=C1,tail(尾)=(C2C3...Cn),即除了第一個類屬於head之外,其他的全屬於tail;
3. 使用 C+(C1C2C3...Cn) = CC1C2...Cn 表示類序列的和;
4. 那么,類C的線型查找序列公式就是類C加上父類的線型查找序列和父類的線型序列的混合merge,符號表示就是:L[C(C1C2...Cn)] = C + merge(L[C1], ... ,L(Cn), C1...Cn);
5. 如果C沒有父類,則L[C] = C
其規則就是:取第一個類序列的head,例如CC1C2...Cn的head就是C,如果這個head不在任何其他序列的tail里面,就把這個head加入到查找序列里面 —— 認為這是一個好head,並且從merge表達式里面去掉這個類;否則的話,就取第二個類序列,判斷這個類序列的head是否合格,如果是個好head,則同樣加入到查找序列,否則,繼續對下一個類序列判斷;直到所有類class都在merge里面被去掉,也就是全部進入查找序列;如果最后還是存在類class不能進去查找序列,則返回Exception。
示例展示:
首先類的繼承關系如下:
O = object類 class F(O): pass class E(O): pass class D(O): pass class C(D,F): pass class B(D,E): pass class A(B,C): pass

我們要算的是A的MRO順序就是:
L[A(B(D(O),E(O)),C(D(O),F(O)))] = A + merge(L[B(D(O),E(O))], L[C(D(O),F(O))], B(D(O),E(O)) C(D(O),F(O))),這個就是根據上面的類C的線型查找序列公式。
- 簡化一下就是 L[A] = A + merge(L[B], L[C], BC)
L[B] = L[B(D(O),E(O))] = B + merge(L[D], L[E], DE)
L[C] = L[C(D(O),F(O))] = C + merge(L[D], L[F], DF)
- 然后繼續划分:
L[D] = L[D(O)] = D + merge(L[O], O) ,由於O本身沒有父類,所以L[O] = O,所以 L[D] = D + merge(O, O) = D + O =DO ----> merge(O, O)表達式中,O都在第一個位置,也就是head,所以O可以放入L[D]的查找序列中了。
同理:L[E] = L[E(O)] = E + merge(L[O], O) = E + O =EO,L[F] = FO
- 所以得到:
L[B] = B + merge(DO, EO, DE),由於此時D不是任何merge序列的tail(因為每次先從merge第一個序列開始,這里第一個就是DO序列,而DO序列的head就是D),所以D可以提出來放入B的查找序列中,並在merge中去掉D,即:L[B] = B + D +merge(O, EO, E) ,同理先判斷O,由於O在EO序列的tail,所以跳到下一個序列EO,而EO序列的head是E,此時E不在任何merge序列的tail,所以E可以提出來,得到 L[B] = B + D + E +merge(O, O) = BDEO。
同理,L[C] = C + merge(DO, FO, DF) = C + D + F + O = CDFO
- 代入L[A]得到:
L[A] = A + merge(BDEO, CDFO, BC) ,先判斷BDEO的head即B,此時DEO為tail,發現B不在任何merge序列的tail,所以提出來得到:L[A] = A + B + merge(DEO, CDFO, C);
判斷DEO的head即D,由於D在CDFO的tail,所以跳過,判斷CDFO的head即C,發現滿足條件,所以提出C,得到:L[A] = A + B + C + merge(DEO, DFO);
同理一次判斷,得到:L[A] = A + B + C + D + merge(EO, FO) = A + B + C + D + E + F + O = ABCDEFO。
- 所以類A的MRO查找序列就是ABCDEFO。
但是!!!!
MRO序列計算也有得不到想要的結果,也就是返回Exception,原文中有個例子:
>>> O = object >>> class X(O): pass >>> class Y(O): pass >>> class A(X,Y): pass >>> class B(Y,X): pass
class C(A, B): pass
L[C] = C + merge(L[A], L[B], AB) = C + merge(A + merge(L[X], L[Y], XY), B + merge(L[Y], L[X], YX), AB) = C + merge(A+merge(XO, YO, XY), B+merge(YO, XO, YX), AB) = C + merge(A+XYO, B+YXO) = C + merge(AXYO, BYXO) = C + A + B + merge(XYO, YXO)
此時,merge(XYO, YXO)中的X和Y既是head又是tail,已經無法優化合並,所以會報錯,Exception。
注意:以上計算中,每個繼承關系中,類的繼承排列順序很重要。
比較復雜吧。。。所以不必要時最好不要多重繼承;例如可以用在子類中分別實例化父類來作為子類的屬性,這樣就可以不用多繼承來實現調用父類的方法,可參考:https://baijiahao.baidu.com/s?id=1660242196992022960&wfr=spider&for=pc
例如:
# 在son中寫入:可以將實例化對象作為屬性放入需要的位置 self.aunt_ = aunt() self.father_ = father()
##
參考:
https://www.cnblogs.com/szy13037-5/articles/9562639.html
https://www.cnblogs.com/silencestorm/p/8404046.html
https://blog.csdn.net/sdzhr/article/details/81084112
https://blog.51cto.com/freshair/2063290
https://www.jianshu.com/p/e188daac678c
https://www.python.org/download/releases/2.3/mro/
https://blog.csdn.net/jonstank2013/article/details/50754834
