《流畅的Python》
# 特殊方法
# 特殊方法的存在是为了被 Python 解释器调用的,你自己并不需要调用它们
# 然而如果是 Python 内置的类型,比如列表(list)、字符串(str)、字节序列(bytearray)
# 等,那么 CPython 会抄个近路,__len__ 实际上会直接返回 PyVarObject 里的 ob_size 属
# 性。PyVarObject 是表示内存中长度可变的内置对象的 C 语言结构体。直接读取这个值比
# 调用一个方法要快很多
import collections
from random import choice
# namedtuple 用来构建只有少数属性,没有方法的对象。比如数据库条目
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, item):
return self._cards[item]
deck = FrenchDeck()
# 因为 __getitem__ 方法把 [] 操作交给了 self._cards 列表,所以我们的 deck 类自动支持切片(slicing)操作
print(deck[12::13])
choice(deck)
# 另外,仅仅实现了 __getitem__ 方法,这一摞牌就变成可迭代的了
for card in deck: # doctest: +ELLIPSIS
print(card)
# 迭代通常是隐式的,譬如说一个集合类型没有实现 __contains__ 方法,那么 in 运算符就会按顺序做一次迭代搜索。
print(Card('Q', 'hearts') in deck)
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
def spades_high(card):
rank_value = FrenchDeck.ranks.index(card.rank)
return rank_value * len(suit_values) + suit_values[card.suit]
for card in sorted(deck, key=spades_high):
print(card)
# 在测试(doctest)中,如果可能的输出过长的话,那么过长的
# 内容就会被如上面例子的最后一行的省略号(...)所替代。此时就需要
# #doctest: +ELLIPSIS 这个指令来保证 doctest 能够通过。
# 模拟数值类型
from math import hypot
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
# 交互式控制台和调试程序(debugger)用 repr 函数来获取字符串表示形式
# __repr__ 和 __str__ 的区别在于,后者是在 str() 函数被使用,或是在用 print 函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
def __abs__(self):
# 求平方和开根
return hypot(self.x, self.y)
# 默认情况下,我们自己定义的类的实例总被认为是真的,除非这个类对 __bool__ 或者 __
# len__ 函数有自己的实现。bool(x) 的背后是调用 x.__bool__() 的结果;如果不存在 __
# bool__ 方法,那么 bool(x) 会尝试调用 x.__len__()。若返回 0,则 bool 会返回 False;否
# 则返回 True
def __bool__(self):
return bool(self.x or self.y)
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)