Python - 協議和鴨子類型


參考:

  1. Fluent_Python - P430
  2. wiki

這里說的協議是什么?是讓Python這種動態類型語言實現多態的方式。

  1. 在面向對象編程中,協議是非正式的接口,是一組方法,但只是一種文檔,語言不對施加特定的措施或者強制實現。
  2. 雖然協議是非正式的,在Python中,應該把協議當成正式的接口。
  3. Python中存在多種協議,用於實現鴨子類型(對象的類型無關緊要,只要實現了特定的協議(一組方法)即可)
  4. 需要成為相對應的鴨子類型,那就實現相關的協議,即相關的__method__。例如實現序列協議(len__和__getitem),這個類就表現得像序列。
  5. 協議是正式的,沒有強制力,可以根據具體場景實現一個具體協議的一部分。例如,為了支持迭代,只需實現__getitem__,不需要實現__len__。
  6. 在Python文檔中,如果看到“文件類對象“(表現得像文件的對象),通常說的就是協議,這個對象就是鴨子類型。這是一種簡短的說法,意思是:“行為基本與文件一致,實現了部分文件接口,滿足上下文相關需求的東西。”

鴨子類型(Duck Typing)

  1. When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck. - James Whitcomb Riley
  2. 不關注對象的類型,而關注對象的行為(方法)。它的行為是鴨子的行為,那么可以認為它是鴨子。

例子1. 讓FrenchDeck類表現的像Python的序列一樣,FrenchDeck就是鴨子類型。

  1. FrenchDeck類是哪個類的子類,是什么類型,都沒有關系。只要提供所需的方法即可,例如表現得像序列一樣。
  2. Python的序列協議只需要__len__和__getitem__兩個方法。
import collections

Card = collections.namedtuple('Card', ['rank','suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]
        
    def __len__(self):
        return len(self._cars)

    def __getitem__(self, position):
        return self._cards[position]

這里,FrenchDeck實現了Python序列協議所需的__len__和__getitem__方法,它就是鴨子類型,表現得和序列一樣。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM