第十一章 - 接口:从协议到抽象基类
接口:从协议到抽象基类
猴子补丁:在运行时修改类或模块而不修改源码
对于Python来说: 协议 == 接口
ABC = Abstract Base Class
抽象类表示接口 —— Bjarne C++之父
抽象基类、描述符和元类都是用于构建框架的工具,因此只有少数Python开发者编写的抽象基类不会对用户施加不必需要的限制,让他们做无用功。
11.2 Python喜欢序列
class Foo(object): def __getitem__(self, item): return range(0, 5)[item] f = Foo() print(f[1]) for i in f: print(i) >>> 1 0 1 2 3 4
11.6 标准库中的抽象基类
Python从2.6开始,标准库提供了抽象基类,大多数抽象基类在collecitons.abc模块中定义。
11.7 定义并使用一个抽象基类
示例11-7
import abc class Tombola(abc.ABC): @abc.abstractmethod def load(self, iterable): """从可迭代对象中添加元素""" @abc.abstractmethod def pick(self): """随机删除元素,然后将其返回 如果实例为空,这个方法应该抛出'LookupError'。 """ def loaded(self): """如果至少有一个元素,返回True,否则返回False""" 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))
注意: 其实抽象方法可以有实现代码,即便实现了,子类也必须覆盖抽象方法,但是在子类中可以使用super()函数调用父类的抽象方法,为它添加功能,而不是从头开始实现。
import random from abstract import Tombola class BingoCage(Tombola): def __init__(self, items): self._randomizer = random.SystemRandom() self._items = [] self.load(items) def load(self, iterable): self._items.extend(iterable) self._randomizer.shuffle(self._items) def pick(self): try: return self._items.pop() except IndexError: raise LookupError("pick from empty BingoCage") def __call__(self, *args, **kwargs): self.pick()