Python抽象类(ABC)浅析
今天在看apscheduler源代码中,看到abc.py这个文件,出于好奇,深入看了一下。
abc的缩写是Abstract Base Classes,翻译就是抽象基类。详细链接可以查看
可以看出这个是类是2007年,由Python创始人Guido van Rossum和Talin一起引入的。
引入目的:
-
重载isinstance()和issubclass()。 增加新模块abc,用作“ ABC支持框架”。 它定义了与ABC一起使用的元类和可用于定义抽象方法的装饰器。 增加支持容器和迭代器的具体ABC,并将添加到集合模块中。
首先我们看功能一:
1、重载isinstance和issubclass.
import abc class PluginBase(object): __metaclass__ = abc.ABCMeta pass PluginBase.register(tuple) print(issubclass(tuple, PluginBase)) print(isinstance((), PluginBase)) # python3 class PluginBase1(metaclass=abc.ABCMeta): pass
可以看到,通过register可以注册一个子类。
2、实现抽象的方法。
# abc_base.py import abc class PluginBase(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def load(self, input): """Retrieve data from the input source and return an object.""" return @abc.abstractmethod def save(self, output, data): """Save the data object to the output.""" return
# abc_test.py import abc from abc_base import PluginBase class RegisteredImplementation(object): def load(self, input): return input.read() def save(self, output, data): return output.write(data) PluginBase.register(RegisteredImplementation) if __name__ == __main__: # Raise Exception # PluginBase() # TypeError: Cant instantiate abstract class PluginBase with abstract methods load, save print Subclass:, issubclass(RegisteredImplementation, PluginBase) print Instance:, isinstance(RegisteredImplementation(), PluginBase) regimpl_instance = RegisteredImplementation() #Subclass: True #Instance: True print(regimpl_instance.load(file("abc_base.py")))
可以测试,不能实例化积累。
3、对容器的支持。
在Python源码 typing.pyi中有下面代码,可以看出来Sized,Hashable里面都有引入ABCMeta
# 部分 class Sized(Protocol, metaclass=ABCMeta): @abstractmethod def __len__(self) -> int: ... @runtime class Hashable(Protocol, metaclass=ABCMeta): # TODO: This is special, in that a subclass of a hashable class may not be hashable # (for example, list vs. object). Its not obvious how to represent this. This class # is currently mostly useless for static checking. @abstractmethod def __hash__(self) -> int: ... @runtime class Iterable(Protocol[_T_co]): @abstractmethod def __iter__(self) -> Iterator[_T_co]: ... @runtime class Iterator(Iterable[_T_co], Protocol[_T_co]): @abstractmethod def next(self) -> _T_co: ... def __iter__(self) -> Iterator[_T_co]: ...
import collections iter = collections.Iterator() 会报错 TypeError: Cant instantiate abstract class Iterator with abstract methods next
我们实现一个斐波拉契数列
import collections class MyFib(collections.Iterator): """ Extend the abstract base class and create Fib sequence object. """ def __init__(self, max): self.max = max self.a = 0 self.b = 1 def next(self): """ This method from the super class must be implemented. :return: the iterator object. """ fib = self.a if fib > self.max: raise StopIteration else: self.a, self.b = self.b, self.a + self.b return fib if __name__ == __main__: my_fib = MyFib(10) print(issubclass(my_fib, collections.Iterable)) # Result, True, reason? because the Iterable is base class of Iterator. print(list(my_fib)) # [0, 1, 1, 2, 3, 5, 8]