想要让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
本文展示了如何通过实现Python特殊方法,如`__len__`和`__getitem__`,使自定义的扑克牌类能够无缝适配Python内置函数和标准库。利用这些方法,我们可以方便地获取牌堆长度、抽取特定位置的牌,并能直接使用`random.choice`来随机抽取一张牌。此外,由于`__getitem__`的支持,扑克牌类还支持列表的切片、迭代和反向迭代操作,极大地提高了代码的灵活性和可读性。
341

被折叠的 条评论
为什么被折叠?



