abc
— 抽象基类¶
源代码: Lib/abc.py
此模块提供在 Python 中定义 抽象基类 (ABC) 的基础架构,如 PEP 3119 中所述;参阅 PEP 了解将其添加到 Python 中的原因。(另请参阅 PEP 3141 和 numbers
模块,了解基于 ABC 的数字类型层次结构。)
collections
模块有一些派生自 ABC 的具体类;当然,这些类可以进一步派生。此外,collections.abc
子模块有一些 ABC,可用于测试类或实例是否提供特定接口,例如,如果它 可散列 或如果它 映射。
此模块提供元类 ABCMeta
用于定义 ABC,以及辅助类 ABC
用于通过继承方式定义 ABC
- class abc.ABC¶
一个辅助类,其元类为
ABCMeta
。有了这个类,可以通过简单地派生自ABC
来创建抽象基类,从而避免有时令人困惑的元类用法,例如from abc import ABC class MyABC(ABC): pass
请注意,
ABC
的类型仍然是ABCMeta
,因此从ABC
继承需要采取关于元类使用的常规预防措施,因为多重继承可能导致元类冲突。也可以通过传递元类关键字并直接使用ABCMeta
来定义抽象基类,例如from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass
在 3.4 版中添加。
- class abc.ABCMeta¶
用于定义抽象基类 (ABC) 的元类。
使用此元类来创建 ABC。ABC 可以直接作为子类,然后充当混合类。还可以将不相关的具体类(甚至是内置类)和不相关的 ABC 注册为“虚拟子类”——内置
issubclass()
函数会将这些类及其子类视为注册 ABC 的子类,但注册 ABC 不会显示在其 MRO(方法解析顺序)中,并且注册 ABC 定义的方法实现不可调用(甚至通过super()
也不可调用)。[1]使用
ABCMeta
元类创建的类具有以下方法- register(subclass)¶
将 subclass 注册为此 ABC 的“虚拟子类”。例如
from abc import ABC class MyABC(ABC): pass MyABC.register(tuple) assert issubclass(tuple, MyABC) assert isinstance((), MyABC)
在 3.3 版中更改: 返回已注册的子类,以允许用作类装饰器。
在 3.4 版中更改: 要检测对
register()
的调用,可以使用get_cache_token()
函数。
还可以在抽象基类中覆盖此方法
- __subclasshook__(subclass)¶
(必须定义为类方法。)
检查 subclass 是否被视为此 ABC 的子类。这意味着可以进一步自定义
issubclass()
的行为,而无需对想要视为 ABC 子类的每个类都调用register()
。(此类方法从 ABC 的__subclasscheck__()
方法调用。)此方法应返回
True
、False
或NotImplemented
。如果它返回True
,则子类会被视为该 ABC 的子类。如果它返回False
,则子类不会被视为该 ABC 的子类,即使它通常是子类。如果它返回NotImplemented
,则将使用常规机制继续进行子类检查。
有关这些概念的演示,请查看此 ABC 定义
class Foo: def __getitem__(self, index): ... def __len__(self): ... def get_iterator(self): return iter(self) class MyIterable(ABC): @abstractmethod def __iter__(self): while False: yield None def get_iterator(self): return self.__iter__() @classmethod def __subclasshook__(cls, C): if cls is MyIterable: if any("__iter__" in B.__dict__ for B in C.__mro__): return True return NotImplemented MyIterable.register(Foo)
ABC
MyIterable
将标准可迭代方法__iter__()
定义为抽象方法。此处给出的实现仍可从子类中调用。get_iterator()
方法也是MyIterable
抽象基类的组成部分,但它不必在非抽象派生类中重写。此处定义的
__subclasshook__()
类方法表示,任何在__dict__
(或通过__mro__
列表访问其基类之一的 __dict__)中具有__iter__()
方法的类也会被视为MyIterable
。最后,最后一行使
Foo
成为MyIterable
的虚拟子类,即使它没有定义__iter__()
方法(它使用旧式可迭代协议,该协议根据__len__()
和__getitem__()
定义)。请注意,这不会使get_iterator
作为Foo
的方法提供,因此它被单独提供。
abc
模块还提供了以下装饰器
- @abc.abstractmethod¶
指示抽象方法的装饰器。
使用此装饰器要求类的元类是
ABCMeta
或派生自它。具有派生自ABCMeta
的元类的类无法实例化,除非它的所有抽象方法和属性被重写。可以使用任何常规“super”调用机制调用抽象方法。abstractmethod()
可用于声明属性和描述符的抽象方法。动态向类添加抽象方法,或尝试在创建后修改方法或类的抽象状态,仅使用
update_abstractmethods()
函数支持。abstractmethod()
仅影响使用常规继承派生的子类;使用 ABC 的register()
方法注册的“虚拟子类”不受影响。当
abstractmethod()
与其他方法描述符结合使用时,它应作为最内层的装饰器应用,如下面的用法示例所示class C(ABC): @abstractmethod def my_abstract_method(self, arg1): ... @classmethod @abstractmethod def my_abstract_classmethod(cls, arg2): ... @staticmethod @abstractmethod def my_abstract_staticmethod(arg3): ... @property @abstractmethod def my_abstract_property(self): ... @my_abstract_property.setter @abstractmethod def my_abstract_property(self, val): ... @abstractmethod def _get_x(self): ... @abstractmethod def _set_x(self, val): ... x = property(_get_x, _set_x)
为了与抽象基类机制正确交互,描述符必须使用
__isabstractmethod__
将自身标识为抽象。通常,如果用于组合描述符的任何方法都是抽象的,则此属性应为True
。例如,Python 的内置property
等同于class Descriptor: ... @property def __isabstractmethod__(self): return any(getattr(f, '__isabstractmethod__', False) for f in (self._fget, self._fset, self._fdel))
注意
与 Java 抽象方法不同,这些抽象方法可能具有实现。可以通过
super()
机制从重写它的类调用此实现。这可能有用,作为使用协作多重继承的框架中的超级调用的端点。
abc
模块还支持以下旧版装饰器
- @abc.abstractclassmethod¶
在 3.2 版中添加。
自 3.3 版起已弃用: 现在可以使用
classmethod
与abstractmethod()
,使此装饰器变得多余。内置
classmethod()
的子类,表示抽象类方法。否则它类似于abstractmethod()
。此特殊情况已弃用,因为当
classmethod()
装饰器应用于抽象方法时,它现在被正确地标识为抽象class C(ABC): @classmethod @abstractmethod def my_abstract_classmethod(cls, arg): ...
- @abc.abstractstaticmethod¶
在 3.2 版中添加。
自版本 3.3 起已弃用:现在可以使用
staticmethod
和abstractmethod()
,这使得此装饰器变得多余。内置
staticmethod()
的子类,表示抽象静态方法。否则,它类似于abstractmethod()
。此特殊情况已弃用,因为当
staticmethod()
装饰器应用于抽象方法时,它现在被正确地标识为抽象class C(ABC): @staticmethod @abstractmethod def my_abstract_staticmethod(arg): ...
- @abc.abstractproperty¶
自版本 3.3 起已弃用:现在可以使用
property
、property.getter()
、property.setter()
和property.deleter()
和abstractmethod()
,这使得此装饰器变得多余。内置
property()
的子类,表示抽象属性。此特殊情况已弃用,因为当
property()
装饰器应用于抽象方法时,它现在被正确地标识为抽象class C(ABC): @property @abstractmethod def my_abstract_property(self): ...
上面的示例定义了一个只读属性;你还可以通过适当标记一个或多个底层方法为抽象来定义一个可读写的抽象属性
class C(ABC): @property def x(self): ... @x.setter @abstractmethod def x(self, val): ...
如果只有部分组件是抽象的,则只需更新这些组件即可在子类中创建具体属性
class D(C): @C.x.setter def x(self, val): ...
abc
模块还提供了以下函数
- abc.get_cache_token()¶
返回当前抽象基类缓存令牌。
令牌是一个不透明对象(支持相等性测试),用于标识虚拟子类的抽象基类缓存的当前版本。每次调用
ABCMeta.register()
时,令牌都会更改任何 ABC。在 3.4 版中添加。
- abc.update_abstractmethods(cls)¶
重新计算抽象类的抽象状态的函数。如果在创建类之后实现了或更改了类的抽象方法,则应调用此函数。通常,此函数应从类装饰器中调用。
返回 cls,以允许作为类装饰器使用。
如果 cls 不是
ABCMeta
的实例,则不执行任何操作。注意
此函数假定 cls 的超类已更新。它不会更新任何子类。
在 3.10 版中添加。
脚注