abc — 抽象基类

源代码: Lib/abc.py


此模块提供在 Python 中定义抽象基类 (ABC) 的基础结构,如 PEP 3119 中所述;有关为何将其添加到 Python 的原因,请参阅该 PEP。(另请参阅 PEP 3141numbers 模块,了解基于 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__() 方法调用的。)

此方法应返回 TrueFalseNotImplemented。如果它返回 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 版弃用: 现在可以使用 classmethodabstractmethod(),使得此装饰器变得冗余。

内置 classmethod() 的子类,指示抽象类方法。否则,它与 abstractmethod() 相似。

此特殊情况已弃用,因为 classmethod() 装饰器在应用于抽象方法时现在被正确识别为抽象的

class C(ABC):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, arg):
        ...
@abc.abstractstaticmethod

在 3.2 版本加入。

自 3.3 版弃用: 现在可以使用 staticmethodabstractmethod(),使得此装饰器变得冗余。

内置 staticmethod() 的子类,指示抽象静态方法。否则,它与 abstractmethod() 相似。

此特殊情况已弃用,因为 staticmethod() 装饰器在应用于抽象方法时现在被正确识别为抽象的

class C(ABC):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(arg):
        ...
@abc.abstractproperty

自 3.3 版弃用: 现在可以使用 propertyproperty.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 版本加入。

脚注