定义抽象基类的子类:
下面的例子:FrenchDeck2
声明为collections. MutableSequence
的子类。
import collections
from random import shuffle
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, key, value):
self._cards[key] = value
def __delitem__(self, position):
del self._cards[position]
def insert(self, position, value):
self._cards.insert(position, value)
1,为了支持洗牌,只需实现 __setitem__
方法。
2,但是继承 MutableSequence
的类必须实现 __delitem__
方法,这是MutableSequence
类的 一个抽象方法。
3,此外,还要实现 insert
方法,这是 MutableSequence
类的第三个抽象方法。
导入时(加载并编译 frenchdeck2.py
模块时) ,Python 不会检查抽象方法的实现,在运行时 实例化 FrenchDeck2
类时才会真正检查。因此,如果没有正确实现某个抽象方法,Python 会 抛出 TypeError
异常,并把错误消息设为
"Can't instantiate abstract class FrenchDeck2 with abstract methods __delitem__,insert"
。正是这个原因,即便 FrenchDeck2
类不需要 __delitem__
和 insert
提供的行为,也要实现,因为 MutableSequence
抽象基类需要它们。
FrenchDeck2 从 Sequence 继承了几个拿来即用的具体方法:__contains__
、__iter__
、 __reversed__
、index
和 count
。FrenchDeck2
从 MutableSequence
继承了append
、extend
、 pop
、remove
和 __iadd__
。
在 collections.abc 中,每个抽象基类的具体方法都是作为类的公开接口实现的,因此不 用知道实例的内部接口。
要想实现子类,我们可以覆盖从抽象基类中继承的方法,以更高效的方式重 新实现。例如,__contains__
方法会全面扫描序列,可是,如果你定义的序 列按顺序保存元素,那就可以重新定义 __contains__
方法,使用 bisect
函 数做二分找,从而提升搜索速度。
标准库中的抽象基类:
从 Python 2.6 开始,标准库提供了抽象基类。大多数抽象基类在 collections.abc
模块中定 义,不过其他地方也有。例如,numbers
和 io
包中有一些抽象基类。但是,collections. abc
中的抽象基类最常用。我们来看看这个模块中有哪些抽象基类。
1,collections.abc模块中的抽象基类:
标准库中有两个名为 abc
的模块,这里说的是 collections.abc
。为了减少加 载时间,Python 3.4 在 collections
包之外实现这个模块(在 Lib/collections abc.py 中,https://hg.python.org/cpython/file/3.4/Lib/_collections_abc.py),因此要 与 collections
分开导入。另一个abc
模块就是abc
(即Lib/abc.py,https:// hg.python.org/cpython/file/3.4/Lib/abc.py),这里定义的是 abc.ABC
类。每个抽象 基类都依赖这个类,但是不用导入它,除非定义新抽象基类。
Python 3.4 在 collections.abc
模块中定义了16 个抽象基类,简要的UML 类图:
和官方文档中这些抽象基类的表格(https://docs.python.org/zh-cn/3.6/library/collections.abc.html):
这里来细说这些抽象基类:
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
提供支持,以一种安全的方式判断对象能不能调用或散列。
若想检查是否能调用,可以使用内置的 callable() 函数;但是没有类似的 hashable()
函数,因此测 试对象是否可散列,最好使用 isinstance(my_obj, Hashable)。
2、numbers 包中的抽象基类:
numbers
包 定义的是“数字塔”(即各个抽 象基类的层次结构是线性的),其中 Number
是位于最顶端的超类
,随后是 Complex
子类, 依次往下,最底端是 Integral
类:
因此,如果想检查一个数是不是整数,可以使用 isinstance(x, numbers.Integral)
,这样 代码就能接受 int、bool
(int 的子类),或者外部库使用 numbers
抽象基类注册的其他类 型。为了满足检查的需要,你或者你的 API 的用户始终可以把兼容的类型注册为 numbers. Integral
的虚拟子类。
与之类似,如果一个值可能是浮点数类型,可以使用 isinstance(x, numbers.Real)
检查。 这样代码就能接受 bool、int、float、fractions.Fraction
,或者外部库(如 NumPy
,它 做了相应的注册)提供的非复数类型。
decimal.Decimal
没有注册为 numbers.Real
的虚拟子类,这有点奇怪。没注 册的原因是,如果你的程序需要 Decimal
的精度,要防止与其他低精度数字 类型混淆,尤其是浮点数。