1,鴨子型別和白鵝型別
1.1,白鵝型別
白鵝型別對介面有明確定義,比如不可變序列(sequence),需要實現__contains__,__iter__,__len__,__getitem__,__reversed__,index,count。
對於其中的抽象方法,子類在繼承時必須具體化,其餘非抽象方法在繼承時可以自動獲得,sequence序列必須具體化的抽象方法是__len__和__getitem__。
from collections import abcclass foo(abc.sequence):
def __init__(self, components):
self._components = components
def __getitem__(self, item):
return self._components[item]
def __len__(self):
return len(self._components)
foo通過繼承abc.sequence,明確了自己的身份,明確了自己需要實現哪些方法,並且會自動獲得哪些方法, 這樣的正式繼承即為白鵝型別。
foo的例項就是正式的sequence類了,可以通過isinstance檢查,可以使用自己建立的抽象方法,可以使用繼承抽象基類獲得的方法:
f = foo(list('abcde'))print(isinstance(f, abc.sequence)) # 結果true
print(f[0]) # 'a',__getitem__
print(len(f)) # 5,__len__
print('b' in f) # true,__contains __
for i in f: # __iter__
print(i)
print(list(reversed(f))) # ['e', 'd', 'c', 'b', 'a'], __reversed__
print(f.count('a')) # 1, count
print(f.index('a')) # 0, index
1.2,鴨子型別
鴨子型別沒有明確的介面,只是遵循了一定的協議,比如python序列協議只需要實現__len__和__getitem__方法,對於序列,這點鴨子型別和白鵝型別中sequence抽象基類的要求相同,但白鵝型別sequence繼承後能夠自動獲得抽象基類的方法,而鴨子型別不會有這些方法:
class foo:def __init__(self, components):
self._components = components
def __getitem__(self, item):
return self._components[item]
def __len__(self):
return len(self._components)
foo自己實現的2個方法可以用:
print(f[0]) # 'a', __getitem__print(len(f)) # 5, __len__
foo自己沒有實現的3個方法不可用:
print(list(reversed(f))) # errorprint(f.count('a')) # error
print(f.index('a')) # error
下面2個自己沒實現的方法也可以用,為什麼呢?原因是python發現某些特殊方法沒有實現時,會自動嘗試呼叫其他特殊方法:
print('b' in f) # 可以用,python的in測試會依次嘗試呼叫__contains__,__iter__,__getitem__for i in f: # 可以用,python的迭代會依次嘗試呼叫__iter__,__getitem__
print(f)
另外鴨子型別的foo,無法通過isinstance檢查:
print(isinstance(f, abc.sequence)) # 結果false
2,抽象基類
抽象基類的主要作用:1)作為超類定義介面;2)isinstance檢查;3)註冊:不子類化就宣告實現介面;4)自動識別:不註冊/不子類化就讓抽象基類能夠識別虛擬子類。
猴子補丁:執行時修改類,不改變原始碼,frenchdeck.__setitem__ = set_card,例如給frenchdeck類補上set_card()方法。
2.1,自己定義抽象基類 - abc
自定義抽象基類需要繼承abc.abc(不繼承無法約束子類必須實現抽象方法),並且用@abc.abstractmethod裝飾抽象方法,抽象基類無法例項化。
子類在基礎抽象基類時,必須將抽象方法實現,否則無法例項化。
import abcclass tombola(abc.abc): # 繼承abc.abc
@abc.abstractmethod # 宣告抽象方法
def load(self):
"""這裡是注釋"""
def inspect(self): # 非抽象方法
pass
2.2,內建抽象基類 - collections.abc
iterable:__iter__
container:__contains__
sized:__len__
sequence:不可變序列
set:不可變集合
callable、hashable
iterator、iterable
可以通過對抽象基類例項化,來檢視強制要求實現的抽象方法:
>>> import collections>>> collections.sequence()
traceback (most recent call last):
file "", line 1, in typeerror: can't instantiate abstract class sequence with abstract methods __getitem__, __len__
2.3,內建抽象基類 - numbers
number:數基類
complex:複數 = 實數 + 虛數
real:實數 = 有理數 + 無理數
rational:有理數 = 整數 + 分數
intergral:整數
例如:判斷浮點數可以用isinstance(obj, numbers.real)
如果obj是bool,int,float,fractions.fraction,或外部庫(例如numpy)提供的非複數型別,都能返回true
用type(obj, float)則比較侷限
2.4,內建抽象基類 — io
定義了io處理相關的抽象基類
2.5,註冊 — 讓基類擁有虛擬子類
通過註冊,可以不用繼承,就建立基類和「虛擬子類」之間的關係。有二個關鍵點:
1)註冊後,python會相信我們,直接讓「虛擬子類」通過isinstance檢查,也不會約束虛擬子類必須實現抽象方法。
2)「虛擬子類」不會從抽象基類獲得任何方法和屬性。
綜上所述:為避免執行時出錯,註冊後我們應該手動讓「虛擬子類」實現「基類」的所有方法。
註冊方法一:裝飾器(python3.3之後支援)
@tombola.register # 裝飾器註冊class tombolist(list):
def load(self):
pass
註冊方法二:標準呼叫語法
class tombolist(list):def load(self):
pass
tombola.register(tombolist) # 標準呼叫語法註冊
註冊後,虛擬子類的例項化物件,就能夠通過isinstance檢查:
t = tombolist()isintance(t, tombola) # true
物件註冊後,如果缺少某些方法尚未實現,會報錯提示
2.6,自動識別
抽象基類有個方法__subclasshook__,通過判斷其他類是否具有一些特殊方法(失敗返回notimplement讓虛擬子類繼續判斷),來識別其他類是不是自己的「虛擬子類」,例如:
class struggle:def __len__(self):
pass
s = struggle()
isintance(s, abc.sized) # true
必須具有__subclasshook__的抽象基類才能提供該功能。
ps:自己建了個類,實現了collections.sequence所有方法,但是無法通過collections.sequence檢查,collections.sequence貌似沒有實現該方法。
guxh的python筆記十一 異常處理
1,抓錯方法 name 0,1,2 try name 3 except indexerror as exc 抓單個錯誤,列印錯誤資訊e print exc except indexerror,keyerror as exc 同時抓多個錯誤,不推薦!print exc except exception...
Python基礎筆記 七
語法 for 變數 in 迭代序列 即列表,元組,字典,集合或字串 迴圈體 for 迴圈訪問列表 list woodman alan bobo for name in list print name 輸出結果 woodman alan bobo 語法 for 變數 in 迴圈序列 迴圈體 else ...
Python學習筆記(七)
深淺拷貝 set 集合 函式a 1,2 3,4 b a.copy print b 結果 1,2 3,4 b 0 0 8 print a 結果 8,2 3,4 print b 結果 8,2 3,4 原理圖 去重關係測試 定義語法 s set in not in 集合等價與不等價 子集 s set al...