abc — 抽象基类

源代码: Lib/abc.py


此模块提供在 Python 中定义 抽象基类 (ABC) 的基础架构,如 PEP 3119 中所述;参阅 PEP 了解将其添加到 Python 中的原因。(另请参阅 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,则子类会被视为该 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 版起已弃用: 现在可以使用 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()

返回当前抽象基类缓存令牌。

令牌是一个不透明对象(支持相等性测试),用于标识虚拟子类的抽象基类缓存的当前版本。每次调用 ABCMeta.register() 时,令牌都会更改任何 ABC。

在 3.4 版中添加。

abc.update_abstractmethods(cls)

重新计算抽象类的抽象状态的函数。如果在创建类之后实现了或更改了类的抽象方法,则应调用此函数。通常,此函数应从类装饰器中调用。

返回 cls,以允许作为类装饰器使用。

如果 cls 不是 ABCMeta 的实例,则不执行任何操作。

注意

此函数假定 cls 的超类已更新。它不会更新任何子类。

在 3.10 版中添加。

脚注