1 還是先看定義 duck typing, 2 鴨子類型是多態(polymorphism)的一種形式.在這種形式中,不管對象屬於哪個, 3 也不管聲明的具體接口是什么,只要對象實現了相應的方法,函數就可以在對象上執行操作. 4 即忽略對象的真正類型,轉而關注對象有沒有實現所需的方法、簽名和語義. 5 duck typing 6 A form of polymorphism where functions 7 operate on any object that implements the 8 appropriate methods, regardless of their 9 classes or explicit interface declarations. 10 11 Wikipedia 是這樣描述 duck typing 的, 12 在計算機語言中, duk typing 是一個類型測試的一個具體應用. 13 是將對類型的檢查推遲到代碼運行的時候,由動態類型(dynamic typing) 14 或者反省(reflection)實現. duck typing 應用在通過應用規則/協議(protocol) 15 建立一個適合的對象 object. 16 '如果它走起步來像鴨子,並且叫聲像鴨子, 那個它一定是一只鴨子.' 17 對於一般類型 normal typing, 假定一個對象的 suitability 只有該對象的類型決定. 18 然而,對於 duck typing 來說, 一個對象 object 的 suitability 是通過該對象是否 19 實現了特定的方法跟屬性來決定 certain methods and properties, 而不是由該對象 20 的來類型決定. 21 22 注, 23 In computer science, reflection is the ability of a computer program to 24 examine,introspect, and modify its own structure and behavior at runtime. 25 26 From Wikipedia, 27 In computer programming, duck typing is an application of the duck test 28 in type safety.It requires that type checking be deferred to runtime, 29 and is implemented by means of dynamic typing or reflection. 30 Duck typing is concerned with establishing the suitability of an object 31 for some purpose, using the principle, "If it walks like a duck and it 32 quacks like a duck, then it must be a duck." With normal typing, 33 suitability is assumed to be determined by an object's type only. 34 In duck typing, an object's suitability is determined by the presence 35 of certain methods and properties (with appropriate meaning), 36 rather than the actual type of the object. 37 38 39 鴨子類型的起源 Origins of duck-typing, 40 現在谷歌工程師,Python 社區重要貢獻者之一: Alex Martelli 說到, 41 我相信是 Ruby 社區推動了 duck typing 這個術語的流行. 42 但是這個duck typing 這種表達在 Ruby 和 Python 火之前, 43 就是在Python 的討論中使用過. 44 45 根據 Wikipedia, duck typing 這一術語最早被 Alex Martelli 在 2000 所使用. 46 Related Link of Wikipedia - https://en.wikipedia.org/wiki/Duck_typing 47 48 歸功於 python 的 數據類型 data model, 你的用戶自定義類型的行為可以像 built-in 類型一樣自然。 49 這並不需要通過繼承 inheritance 來獲得. 本着 duck typing, 可以在對象中只實現需要的方法, 就能 50 保證保證對象的行為符合預期. 對 Python 來說,這基本上是指避免使用 isinstance 檢查對象的類, 51 更別提 type(foo) is bar 這種更糟的檢查方式了,這樣做沒有任何好處,甚至禁止最簡單的繼承方式. 52 具體使用時,上述建議有一個常見的例外:有些 Python API 接受一個字符串或字符串序列; 53 如果只有一個字符串,可以把它放到列表中,從而簡化處理. 因為字符串是序列類型, 54 所以為了把它和其他不可變序列區分開,最簡單的方式是使用 isinstance(x, str) 檢查. 55 另一方面,如果必須強制執行 API 契約,通常可以使用 isinstance 檢查抽象基類。 56 57 在看例子之前, 先看簡略一下兒 協議 protocol 相關內容, 58 在 Python 中創建功能完善的序列類型無需使用繼承, 只需實現符合序列協議的方法. 59 在面向對象編程中,協議是非正式的接口,只在文檔中定義,在代碼中不定義. 60 例如,Python 的序列協議只需要 __len__ 和 __getitem__ 兩個方法. 61 任對象/類型(A)只要使用標准的簽名和語義實現了這兩個方法,就能用在任何期待序列的地方, 62 然而A 是不是哪個類的子類無關緊要,只要提供了所需的方法即可.這就是 python 序列協議. 63 協議是非正式的,沒有強制力,因此如果你知道類的具體使用場景,通常只需要實現一個協議的部分. 64 例如,為了支持迭代,只需實現 __getitem__ 方法,沒必要提供 __len__方法. 65 66 經典示例, duck typing 處理一個字符串 string 或 可迭代字符串 iterable of strings 67 try: #1 68 field_names = field_names.replace(',', ' ').split() #2 69 except AttributeError: #3 70 pass #4 71 field_names = tuple(field_names) #5 72 73 #1, 假定 field_names 是一個字符串 string. EAFP, it’s easier to ask forgiveness than permission 74 #2, 將 field_names 中的 ',' 替換成空格 ' ' 並 split, 將結果放到 list 中 75 #3, sorry, field_names 並不像一個 str, field_names 不能 .replace 或者 .replace 后返回的結果不能 .split() 76 #4, 這里我men假設 新的 field_names 是一個可迭代對象 77 #5, 確保新的 field_names 是一個可迭代對象, 同事保存一個 copy - create 一個 tuple 78 79 field_names = 'abc' #6 80 field_names = 'A,B,C' #7 81 try: 82 field_names = field_names.replace(',', ' ').split() 83 except AttributeError: 84 pass 85 print(field_names) 86 field_names = tuple(field_names) 87 print(field_names) 88 for item in field_names: 89 print(item) 90 91 Output, 92 ['abc'] #6 93 ('abc',) #6 94 abc #6 95 -------------- 96 ['A', 'B', 'C'] #7 97 ('A', 'B', 'C') #7 98 A #7 99 B #7 100 C #7 101 102 結論, 103 Summarize, Outside of frameworks, duck typing is often sim‐pler and more flexible than type checks.
