Python 編程 -- 如何理解鴨子類型(Duck Typing)?通過一個例子來理解
鴨子類型(Duck Typing)可以建立兩個類之間的同類關系,而不需要使用繼承(Inheritance)。這點太重要了。判斷兩個類是否有屬于同一類,鴨子類型做到了“問跡不問心”。“跡”就是方法,“問跡”就是只看這個類是否有另外一個類的方法。而“問心”就是問是否有繼承關系。
兩個類是否可視為同一類?鴨子類型看的是兩個類的方法名是否重合,繼承類型看的是繼承。(繼承,當然會有方法名重合。)鴨子類型本質上放寬了兩個類被視為同一類的條件。
下面用一個例子解釋鴨子類型:

上面的代碼中,我從?Python 自帶的 _collections_abc?模塊中導入了?Container 類, 它包括一個特殊方法, __contains__。 之后,我定義了 EvenNumber 類,它沒有繼承任何東西。 EvenNumber 類只有一個方法,叫做? __contains__。

現在我問一個問題:可不可以把 EvenNumber 與 Container 視為同一類呢, is EvenNumber a subclass of Container?答案是可以。 因為 EvenNumber 包含了 Container 中的抽象方法 __contains__。這就是鴨子類型,關鍵是問你有的方法我有沒有,而不是問我有沒有繼承你。
練習1:猜測定義 EvenNumber 類文件中的帶注釋?(1)、(2)、(3)、(4)、(5)的語句的輸出結果。其中(4)測試 EvenNumber 對象 n 是不是可以看成 Container 對象。(5)測試 EvenNumber 類是不是可以看成 Container 的子類。
練習2:將?EvenNumber 類定義中的 __contains__ 改為 __contain__, 即故意漏掉字母 s,查看帶注釋?(1)、(2)、(3)、(4)、(5)的語句的輸出結果。
要理解鴨子類型的實現機制,必須懂抽象基類(Abstract Base Class)。如果不理解抽象基類中的 __subclasshook__ 特殊方法,則無法理解鴨子類型的實現機制,也無法理解 EvenNumber 定義中帶注釋 (4)與(5) 的語句的輸出結果。
練習3:將上圖代碼中的 “class EvenNumber:”?改為?“class EvenNumber(Container):”,?即讓?EvenNumber 繼承 Container。猜測代碼中的帶注釋?(1)、(2)、(3)、(4)、(5)的語句的輸出結果。
練習4:將上圖代碼中的 “class EvenNumber:” 改為 “class EvenNumber(Container):”, 即讓?EvenNumber 繼承 Container。 將 __contains__ 改為 __contain__, 即故意漏掉字母 s。 測試是否可以生成一個 EvenNumber 對象,n = EvenNumber()。提示:Python 抽象基類規(guī)定,任何繼承它的子類,必須有抽象基類里面的方法(即@abstractmethod 裝飾器下面的方法), 否則無法生成該子類的對象。
更多問題?關注我。