python之协议到抽象基类3
#抽象基类是用于封装框架引入的一般性概念和抽象的,定义抽象基类的子类,先利用现有的抽象基类(collections.MutableSequence),然后自己定义。我们明确把FrenchDeck2 声明为collections.MutableSequence 的子类。
#frenchdeck2.py:FrenchDeck2,collections.MutableSequence 的子类
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck2(collections.abc.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): # 为了支持洗牌,只需实现__setitem__ 方法。
self._cards[position] = value
def __delitem__(self, position): # 但是继承MutableSequence 的类必须实现__delitem__ 方法,这是MutableSequence 类的一个抽象方法。
del self._cards[position]
def insert(self, position, value): # 此外,还要实现insert 方法,这是MutableSequence 类的第三个抽象方法。
self._cards.insert(position, value)
#导入时(加载并编译frenchdeck2.py 模块时),Python 不会检查抽象方法的实现,在运行时实例化FrenchDeck2 类时才会真正检查。因此,如果没有正确实现某个抽象方法,Python 会抛出TypeError 异常,并把错误消息设为"Can't instantiate abstract class FrenchDeck2with abstract methods __delitem__, insert"。正是这个原因,即便FrenchDeck2 类不需要__delitem__ 和insert 提供的行为,也要实现,因为MutableSequence 抽象基类需要它们。
#MutableSequence 抽象基类和collections.abc 中它的超类的UML 类图(箭头由子类指向祖先;以斜体显示的名称是抽象类和抽象方法)
#collections.abc模块中的抽象基类;标准库中有两个名为abc 的模块,这里说的是collections.abc。在collections 包之外实现这个模块(在Lib/_collections_abc.py ),因此要与collections 分开导入。另一个abc 模块就是abc(即Lib/abc.py),这里定义的是abc.ABC 类。每个抽象基类都依赖这个类,但是不用导入它,除非定义新抽象基类。
#collections.abc 模块中定义了16 个抽象基类,简要的UML 类图(没有属性名称)。collections.abc 的官方文档中有个不错的表格(https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes),对各个抽象基类做了总结,说明了相互之间的关系,以及各个基类提供的抽象方法和具体方法(称为“混入方法”)。
#Iterable、Container 和Sized各个集合应该继承这三个抽象基类,或者至少实现兼容的协议。Iterable 通过__iter__方法支持迭代,Container 通过__contains__ 方法支持in 运算符,Sized 通过__len__方法支持len() 函数。
#Sequence、Mapping 和Set这三个是主要的不可变集合类型,而且各自都有可变的子类。MutableSequence ;MutableMapping 和MutableSet 。
#MappingView,在Python 3 中,映射方法.items()、.keys() 和.values() 返回的对象分别是ItemsView、KeysView 和ValuesView 的实例。前两个类还从Set 类继承了丰富的接口,所述的全部运算符。
#Callable 和Hashable这两个抽象基类与集合没有太大的关系,只不过因为collections.abc 是标准库中定义抽象基类的第一个模块,而它们又太重要了,因此才把它们放到collections.abc 模块中。我从未见过Callable 或Hashable 的子类。这两个抽象基类的主要作用是为内置函数isinstance 提供支持,以一种安全的方式判断对象能不能调用或散列。
#Iterator注意它是Iterable 的子类。collections.abc 之后,标准库中最有用的抽象基类包是numbers。