序列协议
class Foo:
def __getitem__(self, pos):
return range(0, 30, 10)[pos]
f = Foo()
# 访问元素
f[1]
# Foo实例可迭代
for i in f: print(i)
# 也能支持in运算
20 in f
15 in f
如果没有__iter__和__contains__方法,Python会调用__getitem__方法,设法让迭代和in运算符可用。
动态协议和猴子补丁
- FrenchDeck对象不支持赋值操作,因此内置的random.shuffle函数不能打乱实例
import collections
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, position):
return self._cards[position]
- 为FrenchDeck打猴子补丁,把它变成可变的,让random.shuffle能够处理
猴子补丁:在运行时修改类或模块,而不改动源码。
def set_card(deck, position, card):
deck._cards[position] = card
FrenchDeck.__setitem__ = set_card
deck = FrenchDeck()
shuffle(deck)
deck[:5]
抽象基类
import abc
class Tombola(abc.ABC):
@abc.abstractmethod
def load(self, iterable):
""""""
@abc.abstractmethod
def pick(self):
""""""
def loaded(self):
return bool(self.inspect())
def inspect(self):
items = []
while True:
try:
items.append(self.pick())
except LookupError:
break
self.load(items)
return tuple(sorted(items))
- 抽象方法中必须有文档字符串,否则会报错
- 实现Tombola具体子类
class LotteryBlower(Tombola):
def __init__(self, iterable):
self._balls = list(iterable)
def load(self, iterable):
self._balls.extend(iterable)
def pick(self):
try:
position = random.randrange(len(self._balls))
except ValueError:
raise LookupError('pick from empty LotteryBlower')
return self._balls.pop(position)
def loaded(self):
return bool(self._balls)
def inspect(self):
return tuple(sorted(self._balls))
- 虚拟子类
注册的类不会从抽象基类中继承任何方法或属性
from random import randrange
@Tombola.register
class TomboList(list):
def pick(self):
if self:
position = randrange(len(self))
return self.pop(position)
else:
raise LookupError('pop from empty TomboList')
load = list.extend
def loaded(self):
return bool(self)
def inspect(self):
return tuple(sorted(self))
issubclass(TomboList, Tombola) # 是Tombola子类
t = TomboList(range(100))
isinstance(t, Tombola) # TomboList实例是Tombola子类实例
# TomboList的超类
TomboList.__mro__
# 没有Tombola,因此没有从中继承任何方法
总结
- 再次提到了序列协议的__getitem__方法
- Python是动态语言,可以在程序运行是用猴子补丁修改类或模块
- 定义抽象类和抽象方法
- 现实抽象子类和虚拟子类
流畅的Python