想要让len()等python原生函数适用于自定义的数据类型,就需要在类中实现python的特殊方法。特殊方法是双下划线开头和结尾的方法。看下面的例子:
#一摞python风格的扑克牌
import collections
Card=collections.namedtuple('Card', ['rank','suit'])
class FrenchDeck:
ranks=[str(n) for n in range(2,11)]+list('JQKA')
suits='spades diamends clubs hearts'.split()
def __init__(self):
self._cards=[Card(rank,suit) for rank in self.ranks
for suit in self.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self,position):
return self._card[position]
In [33]: deck=FrenchDeck()
In [34]: len(deck)
Out[34]: 52
从一摞纸牌中抽取一张特定的牌:deck[0]
。这是由__getitem__
方法提供的:
In [38]: deck[0]
Out[38]: Card(rank='2', suit='spades')
我们需要单独写一个方法随机抽一张牌吗?不需要,python中已经内置了从一个序列中随机选取一个元素的方法random.choice
,我们直接把它用在这一摞纸牌实例上就好:
In [39]: from random import choice
In [40]: choice(deck)
Out[40]: Card(rank='3', suit='spades')
好的,现在我们已经可以体会到在类中实现特殊方法的好处了:
- 使用类的时候不用再记忆自定义的方法名,取而代之的是python通用函数。
- 可以方便的使用python标准库,比如
random.choice
,从而不用重复发明轮子。
好戏还在后面。
因为__getItem__
方法把[]
操作交给了self._cards
列表,所以我们的deck
会自动支持列表的操作,比如切片,迭代,反向迭代。
In [41]: deck[12::13]
Out[41]:
[Card(rank='5', suit='spades'),
Card(rank='8', suit='diamends'),
Card(rank='J', suit='clubs'),
Card(rank='A', suit='hearts')]
In [43]: for card in reversed(deck):
...: print(card)
...:
Card(rank='A', suit='hearts')
Card(rank='A', suit='clubs')
Card(rank='A', suit='diamends')
Card(rank='A', suit='spades')
Card(rank='K', suit='hearts')
...
参考:流畅的python