參考:
- Fluent_Python - P430
- wiki
這里說的協議是什么?是讓Python這種動態類型語言實現多態的方式。
- 在面向對象編程中,協議是非正式的接口,是一組方法,但只是一種文檔,語言不對施加特定的措施或者強制實現。
- 雖然協議是非正式的,在Python中,應該把協議當成正式的接口。
- Python中存在多種協議,用於實現鴨子類型(對象的類型無關緊要,只要實現了特定的協議(一組方法)即可)。
- 需要成為相對應的鴨子類型,那就實現相關的協議,即相關的__method__。例如實現序列協議(len__和__getitem),這個類就表現得像序列。
- 協議是正式的,沒有強制力,可以根據具體場景實現一個具體協議的一部分。例如,為了支持迭代,只需實現__getitem__,不需要實現__len__。
- 在Python文檔中,如果看到“文件類對象“(表現得像文件的對象),通常說的就是協議,這個對象就是鴨子類型。這是一種簡短的說法,意思是:“行為基本與文件一致,實現了部分文件接口,滿足上下文相關需求的東西。”
鴨子類型(Duck Typing)
- 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
- 不關注對象的類型,而關注對象的行為(方法)。它的行為是鴨子的行為,那么可以認為它是鴨子。
例子1. 讓FrenchDeck類表現的像Python的序列一樣,FrenchDeck就是鴨子類型。
- FrenchDeck類是哪個類的子類,是什么類型,都沒有關系。只要提供所需的方法即可,例如表現得像序列一樣。
- 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__方法,它就是鴨子類型,表現得和序列一樣。
