導語:本文章記錄了本人在學習Python基礎之面向對象篇的重點知識及我的心得,打算入門Python的朋友們能夠來一塊兒學習並交流。
本文重點:python
一、不要試圖在內置類型的子類中重寫方法,能夠繼承collections的可拓展類尋求變通;
二、掌握多重繼承中的MRO和Super;
三、瞭解處理多重繼承的一些建議。
內置類能夠子類化,可是內置類型的方法不會調用子類覆蓋的方法。
下面以繼承dict的自定義子類重寫__setitem__爲例說明:segmentfault
class ModifiedDict(dict): def __setitem__(self, key, value): super().__setitem__(key,[value]*2) a=ModifiedDict(one=1) a["two"]=2 print(a) a.update(three=3) print(a)#輸出{'one': 1, 'two': [2, 2], 'three': 3}
從輸出能夠看到,鍵值對one=1和three=3存入a時均調用了dict的__setitem__,只有[]運算符會調用咱們預先覆蓋的方法。
問題的解決方式在於不去子類化dict,而是子類化colections.UserDict。數據結構
用戶自定義的類應該繼承collections模塊,如UserDict,UserList,UserString。這些類作了特殊設計,所以易於拓展。
子類化UserDict的代碼以下:app
from collections import UserDict class ModifiedDict(UserDict): def __setitem__(self, key, value): super().__setitem__(key,[value]*2) b=ModifiedDict(one=1) b["two"]=2 b.update(three=3) print(b)#輸出{'one': [1, 1], 'two': [2, 2], 'three': [3, 3]}
小結:上述問題只發生在C語言實現的內置類型子類化狀況中,並且隻影響直接繼承內置類型的自定義類。相反,子類化使用Python編寫的類,如UserDict或MutableMapping就不會有此問題。
框架
在多重繼承中存在不相關的祖先類實現同名方法引發的衝突問題,這種問題稱做「菱形問題」。Python依靠特定的順序遍歷繼承圖,這個順序叫作方法解析順序。如圖,左圖是類的UML圖,右圖中的虛線箭頭是方法解析順序。
函數
提到類的屬性__mro__,就會提到super:學習
super 是個類,既不是關鍵字也不是函數等其餘數據結構。
做用:super是子類用來調用父類方法的。
語法:super(a_type, obj);
a_type是obj的__mro__,固然也能夠是__mro__的一部分,同時issubclass(obj,a_type)==truespa
舉個例子, 有個 MRO: [A, B, C, D, E, object]
咱們這樣調用:super(C, A).foo()
super 只會從 C 以後查找,即: 只會在 D 或 E 或 object 中查找 foo 方法。設計
下面構造一個菱形問題的多重繼承來深化理解:code
class A: def ping(self): print("A-ping:",self) class B(A): def pong(self): print("B-pong:",self) class C(A): def pong(self): print("C-PONG:",self) class D(B, C): def ping(self): print("D-ping:",self) super().ping() def pingpong(self): self.ping() super().ping() self.pong() super(B,D).pong(self) d=D() d.pingpong() print(D.mro())
輸出以下:
D-ping: <__main__.D object at 0x000001B77096EAC8> A-ping: <__main__.D object at 0x000001B77096EAC8>#前兩行對應self.ping()。 A-ping: <__main__.D object at 0x000001B77096EAC8>#此處super調用父類的ping方法。 B-pong: <__main__.D object at 0x000001B77096EAC8> C-PONG: <__main__.D object at 0x000001B77096EAC8>#此處從B以後搜索父類的pong() [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]#類D的__mro__,數據以元組的形式存儲。
分析:d.pingpong()執行super.ping(),super按照MRO查找父類的ping方法,查詢在類B到ping以後輸出了B.ping()。
(1)把接口繼承和實現繼承區分開;
(2)使用抽象基類顯式表示接口;
(3)經過混入重用代碼;
混入類爲多個不相關的子類提供方法實現,便於重用,但不會實例化。而且具體類不能只繼承混入類。
(4)在名稱中明確指明混入;
Python中沒有把類聲明爲混入的正規方式,Luciano推薦在名稱中加入Mixin後綴。如Tkinter中的XView應變成XViewMixin。
(5)抽象基類能夠做爲混入,反過來則不成立;抽象基類與混入的異同:
(6)不要子類化多個具體類;
具體類能夠沒有,或者至多一個具體超類。
例如,Class Dish(China,Japan,Tofu)中,若是Tofu是具體類,那麼China和Japan必須是抽象基類或混入。
(7)爲用戶提供聚合類;
聚合類是指一個類的結構主要繼承自混入,自身沒有添加結構或行爲。Tkinter採納了此條建議。
(8)優先使用對象組合,而不是類繼承。
優先使用組合能夠令設計更靈活。
組合和委託能夠代替混入,但不能取代接口繼承去定義類型層次結構。
注:super調用知識引自
做者: mozillazg
連接:https://segmentfault.com/a/11...