本章通过代码讨论一个概念: 把protocol当成一个正式接口。协议概念和鸭子类型的关系。当创建自定义类型时,它的实际影响。
我们的实现Vector的策略是使用composition(组合),而不是继承。
协议是非正式的接口,只在文档内定义,在代码中不定义。
例如,序列协议在Python只需要__len__, __getitem__方法。任何类只要实现了这2个方法,它的实例就能当序列用。
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._cards) def __getitem__(self, position): return self._cards[position]
>>> import linshi >>> d = linshi.FrenchDeck() >>> d <linshi.FrenchDeck object at 0x1041796d0> >>> len(d) 52 >>> d[0] Card(rank=‘2‘, suit=‘spades‘)
因为在FrenchDeck类中定义了序列协议的2个方法。所以它的实例就能使用序列类型的2个方法。
即使FrenchDeck类是Object的子类,但因为实现了序列协议,就可以把它当成一个序列类型。
这就是鸭子类型。拥有鸭子的行为,那么就把它当成鸭子。
protocols是非正式的非强制的,所以可以只实现一个协议的部分。
可切片的序列
__getitem__实现了切片功能:
>>> d[0:2] [Card(rank=‘2‘, suit=‘spades‘), Card(rank=‘3‘, suit=‘spades‘)]
但是,返回的是一个list,而不是FrenchDeck实例。因此就不能使用FrenchDeck的其他实例方法。
那些内置序列类型,切片后返回的都是一个新的原本类型的实例,而不是其他类型。所以需要优化__getitem__方法中的代码。
《流畅的Python》 Sequence Hacking, Hashing and Slicing(没完成)
原文:https://www.cnblogs.com/chentianwei/p/12051345.html