abc
— 抽象基类¶
源代码: Lib/abc.py
此模块为在 Python 中定义抽象基类 (ABC) 提供了基础架构,如 PEP 3119 中所述;请参阅 PEP 以了解为什么将其添加到 Python 中。(另请参阅 PEP 3141 以及关于基于 ABC 的数字类型层次结构的 numbers
模块。)
collections
模块有一些派生自 ABC 的具体类;当然,这些类可以进一步派生。此外,collections.abc
子模块有一些 ABC,可用于测试类或实例是否提供了特定的接口,例如,它是否是可哈希的或是否是映射。
此模块提供用于定义 ABC 的元类 ABCMeta
和辅助类 ABC
,以通过继承来替代定义 ABC
- class abc.ABC¶
一个以
ABCMeta
作为其元类的辅助类。 使用此类,可以通过简单地从ABC
派生来创建抽象基类,从而避免有时令人困惑的元类用法,例如from abc import ABC class MyABC(ABC): pass
请注意,
ABC
的类型仍然是ABCMeta
,因此,从ABC
继承需要通常的关于元类用法的预防措施,因为多重继承可能会导致元类冲突。 也可以通过传递 metaclass 关键字并直接使用ABCMeta
来定义抽象基类,例如from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass
3.4 版本中新增。
- class abc.ABCMeta¶
用于定义抽象基类 (ABC) 的元类。
使用此元类创建 ABC。 ABC 可以直接子类化,然后充当 mix-in 类。 您还可以将不相关的具体类(甚至是内置类)和不相关的 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()
机制调用此实现。这对于在框架中使用协作多重继承的情况下,作为超级调用的终点非常有用。
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 版本新增。
脚注