直接看代码吧,比较重要的东西都做了注释。
import collections
from random import choice
'''namedtuple加入到Python里,用以构建只有少数属性但没有方法的对象'''
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in list(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]
deck = FrenchDeck()
'''对于一个类,它和集合一样可以使用len()来查看长度'''
print(len(deck)) # 52
'''获取指定元素都由__getitem__方法提供'''
print(deck[0]) # Card(rank='2', suit='spades')
print(deck[-1]) # Card(rank='A', suit='hearts')
'''Python内置了从一个序列中随机选出一个元素的函数random.choice'''
print(choice(deck)) # Card(rank='2', suit='hearts')
print(choice(deck)) # Card(rank='4', suit='clubs')
print(choice(deck)) # Card(rank='K', suit='diamonds')
'''因为__getitem__方法把[]操作交给了self._cards列表,所以deck类自动支持切片(slicing)操作'''
print(deck[:3]) # [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
print(deck[12::13]) # [Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
'''因为实现了__getitem__方法,deck就变成可迭代的了'''
for card in deck:
print(card) # ...
# Card(rank='6', suit='spades')
# Card(rank='7', suit='spades')
# Card(rank='8', suit='spades')
# Card(rank='9', suit='spades')
# ...
# 反向迭代
for card in reversed(deck):
print(card)
'''给纸牌排序'''
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
def spades_high(card):
rank_value = FrenchDeck.ranks.index(card.rank) # FrenchDeck中rank的索引,比如2的索引是0
return rank_value * len(suit_values) + suit_values[card.suit]
for card in sorted(deck, key=spades_high):
print(card) # Card(rank='2', suit='clubs')
# Card(rank='2', suit='diamonds')
# Card(rank='2', suit='hearts')
# Card(rank='2', suit='spades')
# Card(rank='3', suit='clubs')
# Card(rank='3', suit='diamonds')
# Card(rank='3', suit='hearts')
# Card(rank='3', suit='spades')
# ...
总结:
1. __len__和__getitem__是很重要的方法,也是很好用的。例如,在深度学习中,如果要构建自己的数据集就会使用到这两种方法。作者利用这个程序来告诉大家,Python和别的语言的不同之处。如书中所言:“没有my_object.__len__()这种写法,而应该使用len(my_object),如果my_object是一个自定义类的对象,那么Python会自己调用由你实现的__len__方法。”
from math import hypot
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
def __abs__(self):
return hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
v1 = Vector(2, 4)
v2 = Vector(2, 1)
print(v1 + v2) # Vector(4, 5)
v = Vector(3, 4)
print(abs(v)) # 5.0
print(v * 3) # Vector(9, 12)
print(abs(v * 3)) # 15.0
设置断点就能清楚地看见每个函数的调用过程。