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__() 方法调用的。)

此方法应返回 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() 机制调用此实现。这对于在框架中使用协作多重继承的情况下,作为超级调用的终点非常有用。

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 版本新增。

脚注