我们将遵循 Martelli 的建议,先利用现有的抽象基类(collections.MutableSequence),然后再斗胆自己定义。在示例 11-8 中,我们明确把 FrenchDeck2 声明为 collections.MutableSequence 的子类。
示例 11-8 frenchdeck2.py:
FrenchDeck2,collections.MutableSequence的子类
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck2(collections.MutableSequence):
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]
def __setitem__(self, position, value): # ➊
self._cards[position] = value
def __delitem__(self, position): # ➋
del self._cards[position]
def insert(self, position, value): # ➌
self._cards.insert(position, value)
❶ 为了支持洗牌,只需实现 __setitem__ 方法。
❷ 但是继承 MutableSequence 的类必须实现 __delitem__ 方法,这是 MutableSequence 类的一个抽象方法。
❸ 此外,还要实现 insert 方法,这是 MutableSequence 类的第三个抽象方法。
导入时(加载并编译 frenchdeck2.py 模块时),Python 不会检查抽象方法的实现,在运行时实例化 FrenchDeck2 类时才会真正检查。因此,如果没有正确实现某个抽象方法,Python 会抛出 TypeError 异常,并把错误消息设为"Can't instantiate abstract class FrenchDeck2 with abstract methods __delitem__, insert"。正是这个原因,即便 FrenchDeck2 类不需要 __delitem__ 和 insert 提供的行为,也要实现,因为 MutableSequence 抽象基类需要它们。
如图 11-2 所示,Sequence 和 MutableSequence 抽象基类的方法不全是抽象的。

图 11-2:MutableSequence 抽象基类和 collections.abc 中它的超类的 UML 类图(箭头由子类指向祖先;以斜体显示的名称是抽象类和抽象方法)
FrenchDeck2 从 Sequence 继承了几个拿来即用的具体方法:__contains__、__iter__、__reversed__、index 和 count。FrenchDeck2 从 MutableSequence 继承了 append、extend、pop、remove 和__iadd__。
在 collections.abc 中,每个抽象基类的具体方法都是作为类的公开接口实现的,因此不用知道实例的内部接口。
要想实现子类,我们可以覆盖从抽象基类中继承的方法,以更高效的方式重新实现。例如,
__contains__方法会全面扫描序列,可是,如果你定义的序列按顺序保存元素,那就可以重新定义__contains__方法,使用bisect函数做二分查找(参见 2.8 节),从而提升搜索速度。
为了充分使用抽象基类,我们要知道有哪些抽象基类可用。接下来介绍集合抽象基类。