__getitem__ AND __len__ 方法¶
下面看一个生成扑克牌以及对其进行操作的例子
In [27]:
import collections
Card = collections.namedtuple('Card', ['rank', 'suit']) #'Card' 是 namedtuple 名字, 后面是元素
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): #代表了 self._cards 的 []运算符
return self._cards[position]
In [28]:
beer_card = Card('7', 'diamonds')
beer_card
Out[28]:
In [29]:
deck = FrenchDeck()
len(deck) #默认调用了 __len__()
Out[29]:
In [30]:
deck[0] #调用了 __getitem__()
Out[30]:
In [31]:
deck[-1]
Out[31]:
In [32]:
from random import choice
choice(deck)
Out[32]:
In [33]:
deck[:3] #因为 __getitem__() 方法把 [] 操作交给 slef._cards 列表,我们的 desk 自动支持切片操作
Out[33]:
In [34]:
deck[12::13]
Out[34]:
迭代¶
In [35]:
# 我们只要写了 __getitem__() 方法,就可以将类变成可迭代的
for card in deck[:10]:
print(card)
In [36]:
# 也可以反向迭代
for card in reversed(deck):
print(card)
in 运算符¶
迭代通常是隐式的,如果集合中没有 __contains__ 方法, in 操作符就会按顺序进行一次迭代搜索,于是 in 可以在 FrenchDeck 类中使用,因为它是可迭代的
In [37]:
Card('Q', 'hearts') in deck
Out[37]:
In [38]:
Card('Q', 'beasts') in deck
Out[38]:
排序¶
扑克牌一般按照数字大小( A 最大)来排列,花色按照黑桃(最大),红心,方块,梅花(最小)来排序,我们实现一下此功能
In [39]:
suit_values = dict(spades = 3, hearts = 2, diamonds = 1, clubs = 0)
suit_values
Out[39]:
In [44]:
def spades_high(card):
rank_value = FrenchDeck.ranks.index(card.rank)
return rank_value * len(suit_values) + suit_values[card.suit]
In [45]:
for card in sorted(deck, key=spades_high):
print(card)
现在还无法洗牌,因为牌的顺序是不可变的,除非我们破坏这个类的封装性,直接对 _cards 进行操作,在以后我们会讲到,其实只需要一行代码实现 __setitem__ 方法就可以了