abc
— 抽象基类¶
源代码: Lib/abc.py
此模块提供在 Python 中定义抽象基类 (ABC) 的基础结构,如 PEP 3119 中所述;有关为何将其添加到 Python 的原因,请参阅该 PEP。(另请参阅 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
,则 subclass 被视为此 ABC 的子类。如果它返回False
,则 subclass 不被视为此 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__
列表访问)具有__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()
机制从重写它的类中调用。这对于使用协作式多重继承的框架中的 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()¶
返回当前的抽象基类缓存令牌。
该令牌是一个不透明对象(支持相等性测试),用于标识虚拟子类抽象基类缓存的当前版本。每次在任何 ABC 上调用
ABCMeta.register()
时,令牌都会更改。在 3.4 版本加入。
- abc.update_abstractmethods(cls)¶
一个用于重新计算抽象类的抽象状态的函数。如果类的抽象方法在创建后已被实现或更改,则应调用此函数。通常,此函数应从类装饰器内部调用。
返回 cls,允许作为类装饰器使用。
如果 cls 不是
ABCMeta
的实例,则不执行任何操作。备注
此函数假定 cls 的超类已更新。它不更新任何子类。
在 3.10 版本加入。
脚注