一. 鴨子類型和多態
1.什么是鴨子類型:
在程序設計中,鴨子類型(英語:Duck typing)是動態類型和某些靜態語言的一種對象推斷風格。"鴨子類型"像多態一樣工作,但是沒有繼承。“鴨子類型”的語言是這么推斷的:一只鳥走起來像鴨子、游起泳來像鴨子、叫起來也像鴨子,那它就可以被當做鴨子。也就是說,它不關注對象的類型,而是關注對象具有的行為(方法)。
可以看出,Cat,Dog,Duck中有相同的方法say(),當有一個函數調用Duck類時并調用say()方法,我們傳入Cat類和Dog類也行,函數并不會檢查對象是不是Duck,而是只要你有這樣的方法就能運行。
如,列表的extend()方法只要參數是一個可迭代的對象就可以(list,set,tuple)
還有前面的例子,只要實現了類中的__getitem__()魔法函數,就可以把類當作一個collection,實現啊__iter__和__next__就可以當作一個iterator。python中的鴨子類型允許我們使用任何提供所需方法的對象,而不需要迫使它成為一個子類。
2.多態:
由于python屬于動態語言,當你定義了一個基類和基類中的方法,并編寫幾個繼承該基類的子類時,由于python在定義變量時不指定變量的類型,而是由解釋器根據變量內容推斷變量類型的(也就是說變量的類型取決于所關聯的對象),這就使得python的多態不像是c++或java中那樣,定義一個基類類型變量而隱藏了具體子類的細節。
二. 抽象基類(abc模塊)
1.在某些情況下判斷某個對象的類型:
?
2.強制某個子類必須實現某些方法:
3.模擬抽象基類:
? 3.1利用內置拋錯模擬:(但只有調用某些方法時才會拋異常)
1 class CacheBase(): 2 def get(self,key): 3 #默認拋出異常(Python內置錯誤) 4 raise NotImplementedError 5 def set(self,key,value): 6 raise NotImplementedError 7 #繼承重寫就不會拋異常 8 class Rediscatche(CacheBase): 9 def get(self,key): 10 pass 11 def set(self,key,value): 12 pass 13 cachebase=Rediscatche() 14 cachebase.set("key","value")
3.2利用內置的abc模塊:
?
3.3通用的抽象基類(collections.abc模塊,推薦使用多繼承mixin,以防抽象基類設計過度):
有可遍歷,可哈希的等等抽象基類
?
?
? ?這些抽象基類都有一個魔法函數__subclasshook__():
作用:Comp()沒有繼承Sized,但是卻能判斷出是Sized類型。
__subclasshook__()會判斷傳入的C是否有“__len__”這個方法,有就返回為True
?
?
?
?
三. 使用isintance而不是type
isinstance內部會去檢查它的繼承鏈,就可以判斷它是A的類型,而type是指向B那個對象,判斷是否和B是同一個對象。盡量應使用isinstance,而不是type,以免誤判。
is和==:
is是判斷兩者是不是一個對象(即id是否相同),而==是判斷值是否相同。如type(b)指向的是B這個對象,雖然B繼承于A,但是A和B是兩個不同的對象。? ? ? ? ?
四. 類變量和對象變量
注:1.魔法函數__init__中self是實例化對象,中的參數是對象變量,在實例化后調用變量是向上查找(即先查找對象變量,后查找類變量),類變量可以直接通過類訪問;
2.類變量是所有實例共享的
通過類修改類變量
通過實例對象修改變量
五. 類屬性和實例屬性以及查找順序
1.向上查找,即先查找對象變量(實例屬性),后查找類屬性:
2.多繼承采用MRO(【Method Resolution Order】:方法解析順序)算法:
Python語言包含了很多優秀的特性,其中多重繼承就是其中之一,但是多重繼承會引發很多問題,比如二義性,Python中一切皆引用,這使得他不會像C++一樣使用虛基類處理基類對象重復的問題,但是如果父類存在同名函數的時候還是會產生二義性,Python中處理這種問題的方法就是MRO。
DFS:深度優先算法,這樣查詢順序為A->B->D->C->E
?
?這樣就會出現問題(菱形繼承):如果C繼承D覆蓋D中的某方法,在調用時是先查詢D,然后查詢C,則查詢的方法是D中的,而不是C中重寫的,因此在Python2
后改成了廣度優先的算法。
廣度優先算法,這就解決了菱形繼承,但是在第一種又出現了問題
如D和C中如果有個同名的方法,則會調用C中的方法,而不是D中的,而B是繼承D的,因此從Python2.3后都統一為C3算法
3.C3算法(參考:https://www.cnblogs.com/LLBFWH/p/10009064.html):
求某一類在多繼承中的繼承順序:
類的mro == [類] + [父類的繼承順序] + [父類2的繼承順序]
如果從左到右的第一個類在后面的順序中出現,那么就提取出來到mro順序中
[ABCD] + [EO] --> A = [BCD] + [EO]
如果從左到右的第一個類在后面的順序中出現,且在后面的順序中也是第一位,那么就提出來到mro順序中
[ABCD] + [AEO] --> A = [BCD] + [EO]
如果從左到右的第一個類在后面的順序中出現,但不是在第一位,那么應該繼續往后找,找到符合規則的項目
[ABCD] + [EAO] --> E = [ABCD] + [AO]
[ABCD] + [EAO] + [GEO] --> G = [ABCD] + [EAO] + [EO]
[ABCD] + [EAO] + [EO] --> GE = [ABCD] + [AO] + [O]
關鍵結論:
這個類沒有發生繼承,他的順序永遠是[類o]
? 只要是單繼承,不是多繼承,那么mro順序就是從子類到父類的順序
4.查找順序:
4.1菱形繼承:(Python2.3以前為經典類,默認不繼承object(D),而2.3以后為新式類,默認繼承object,即最后查找object類)
4.2分別繼承:
?
?
六. 靜態方法、類方法以及對象方法
1.實例方法:self為實例對象
?
2.靜態方法:(相當于普通的函數)
(注:采用硬編碼,如果類名改變,相應的靜態方法中也要改變,如下面的Date改變,則parse_from_string中Date也相應改變)
利用外部對參數處理傳入(每次都需要處理,麻煩)
利用靜態方法
靜態方法用處:如在判斷傳入的參數是否為合法字符串,這是不用返回類對象,因此不用傳入類(類方法)
?
3.類方法:(傳遞的是類cls)
注:相比靜態方法,不是采用硬編碼,無論類名稱是什么,都不用修改類方法,且傳遞的是類(cls,只是名稱,可以修改)
?
七. 數據封裝和私有屬性
1.私有屬性:
無法實例或類直接訪問私有屬性,只有通過類中的公共方法get_age間接訪問
2.私有屬性原理:
把具有雙下劃線的屬性(如__birthday變為[_classname__attr]即_User__birthday),因此不是從語言層面解決了絕對私有性,只是加了一些小技巧。主要只是讓我們書寫更加規范,沒有絕對的安全,也可以解決同樣的變量名沖突的問題。如另一個類繼承User,且也有__birthday,則根據規則是不一樣的
仍然能訪問
八. python對象的自省機制
1.概念:
自省是通過一定的機制查詢到對象的內部結構
2.__dict__,dir的使用:
2.1通過dict查找屬性:
實例的屬性,但是通過name屬性卻能查找到(向上查找,name屬性User類這個對象)
?
類屬性,含有模板,文檔,屬性,弱引用等
2.2通過__dict__添加修改屬性:
?
? 2.3通過dir查找屬性(會列出所有屬性,比__dict__更加詳細):
只有屬性名稱,沒有屬性值,還可以對list等使用
九. super函數
1.如果想調用A中的構造函數:
Python2:super(B,self).__init__()
Python3中:super().__init__()
2.既然重寫A的構造函數,為什么還要調用super:
很好的重用代碼,如某個參數需要父類的構造函數處理,就可以調用super函數把參數交給父類的構造函數處理
將name交給Thread的構造函數處理
3.super執行順序:
super并不是直接調用父類,而是根據MRO算法的調用順序(因此先是C,然后是A)
?
十. django rest framework中對多繼承使用的經驗
1.建議:
盡量不要使用多繼承,以免造成混亂
2.mixin多繼承案例(如django restframework中的mixins):
1.mixin類功能單一;
2.不和基類關聯,可以和任意基類組合,基類可以不和mixin關聯就能初始化成功;
3.在mixin中不要使用super函數;
4.盡量以Mixin結尾
十一.python中的with語句
1.try...except語句:
except語句中將2壓入堆棧中,finally又將4壓入堆棧中,所以在取數據時直接從棧頂取數據,因此是4,如果沒有finally則是前面的(如果要操作數據庫,文件等,就需要在try中,except,finally中書寫關閉連接,文件的邏輯)。
2.上下文管理器:
上下文管理器協議(需要實現兩個魔法函數__enter__和__exit__):
需要在__enter__中獲取資源,在__exit__釋放資源,只要滿足這個協議就可以用with語句使用
?
?
十二. contextlib實現上下文管理器
相當于簡化__enter__和__exit__:@contextlib.contextmanager裝飾器將__enter__和__exit__合起來并進行了一系列操作
?
十三.參考文獻:
MRO算法介紹