第一章 - Python数据模型
1.1 一摞Python风格的纸牌
知识点: collections.nametuple
import collections from pprint import pprint Card = collections.namedtuple("card", ("rank", "suit")) class FrenchDeck(object): ranks = [str(n) for n in range(2, 11)] + list("JQKA") # 字符串拼接“2345678910JQKA” suits = "spades diamonds clubs hearts".split() # 四种花色 黑桃,方片,梅花,红心 def __init__(self): self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks] # 利用列表生成式生成52张花色的扑克牌 def __len__(self): return len(self._cards) def __getitem__(self, position): return self._cards[position] deck = FrenchDeck() pprint(list(deck))
运行结果:

[card(rank='2', suit='spades'), card(rank='3', suit='spades'), card(rank='4', suit='spades'), card(rank='5', suit='spades'), card(rank='6', suit='spades'), card(rank='7', suit='spades'), card(rank='8', suit='spades'), card(rank='9', suit='spades'), card(rank='10', suit='spades'), card(rank='J', suit='spades'), card(rank='Q', suit='spades'), card(rank='K', suit='spades'), card(rank='A', suit='spades'), card(rank='2', suit='diamonds'), card(rank='3', suit='diamonds'), card(rank='4', suit='diamonds'), card(rank='5', suit='diamonds'), card(rank='6', suit='diamonds'), card(rank='7', suit='diamonds'), card(rank='8', suit='diamonds'), card(rank='9', suit='diamonds'), card(rank='10', suit='diamonds'), card(rank='J', suit='diamonds'), card(rank='Q', suit='diamonds'), card(rank='K', suit='diamonds'), card(rank='A', suit='diamonds'), card(rank='2', suit='clubs'), card(rank='3', suit='clubs'), card(rank='4', suit='clubs'), card(rank='5', suit='clubs'), card(rank='6', suit='clubs'), card(rank='7', suit='clubs'), card(rank='8', suit='clubs'), card(rank='9', suit='clubs'), card(rank='10', suit='clubs'), card(rank='J', suit='clubs'), card(rank='Q', suit='clubs'), card(rank='K', suit='clubs'), card(rank='A', suit='clubs'), card(rank='2', suit='hearts'), card(rank='3', suit='hearts'), card(rank='4', suit='hearts'), card(rank='5', suit='hearts'), card(rank='6', suit='hearts'), card(rank='7', suit='hearts'), card(rank='8', suit='hearts'), card(rank='9', suit='hearts'), card(rank='10', suit='hearts'), card(rank='J', suit='hearts'), card(rank='Q', suit='hearts'), card(rank='K', suit='hearts'), card(rank='A', suit='hearts')]
1.2 如何使用特殊方法
__len__方法: 如Python内置类型 list, str, bytearray 等, CPython会抄近路,__len__实际上会直接返回PyVarObject里的ob_size属性;PyVarObject是表示内存中长度可变的内置对象的C语言结构体, 直接读取这个值比调用一个方法要快很多。
很多时候,特殊方法的调用是隐式的,比如for i in x: 这个语句背后其实用的是iter(x), 而这个函数的背后则是x.__iter__()方法。
1.2.1 模拟数值类型
from math import hypot # 计算直角三角形的斜边长 class Vector(object): 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 __add__(self, other): # 实现加法运算 x = self.x + other.x y = self.y + other.y return Vector(x, y) def __mul__(self, other): return Vector(self.x * other, self.y * other) def __bool__(self): return bool(self.x or self.y) v1 = Vector(2, 4) v2 = Vector(2, 1) print(v1 + v2) v = Vector(3, 4) print(abs(v)) print(v.__bool__())
print(v)
运行结果:
Vector(4, 5) 5.0 True Vector(3, 4)
1.2.2 字符串表示形式
__repr__与__str__; repr是给机器看的,str是给人看的。
print(repr("0")) >>> '0' print(str("0")) >>> 0
__repr__返回的字符串应该准确、无歧义, 以方便我们调试和记录日志。
1.2.4 自定义的布尔值
默认情况下,我们自己定义的类的实例总是被认为是真的,除非这个类对__bool__或者__len__方法有自己的实现。 bool(x)的背后是调用 x.__bool__()的结果;如果不存在__bool__方法,那么boo(x)会尝试调用x.__len__()。若返回0,则bool会返回False,否则返回True.
class BoolTest(object): def __init__(self, x, y): self.x = x self.y = y def __bool__(self): return True if self.x or self.y else False def __len__(self): return True a = BoolTest(0, 0) b = BoolTest(0, 1) print(bool(a)) print(bool(b)) 运行结果: False True
如果想让__bool__更高效,可以直接使用:
def __bool__(self): return bool(self.x or self.y)
1.3 特殊方法一览表