Skip to main content

29.7. abc —抽象基类

源代码: Lib/abc.py


该模块提供了用于在Python中定义 抽象基类 (ABC)的基础结构,如 PEP 3119 中所述;请参阅PEP为什么这被添加到Python。 (关于基于ABC的数字的类型层次结构,参见 PEP 3141numbers 模块。)

collections 模块有一些从ABCs派生的具体类;这些当然可以进一步衍生。此外,collections.abc 子模块具有一些可用于测试类或实例是否提供特定接口的ABC,例如,它是哈希表还是映射。

此模块提供以下类:

class abc.ABCMeta

定义抽象基类(ABC)的元类。

使用此元类创建ABC。 ABC可以直接子类化,然后充当混合类。您还可以将无关的具体类(甚至内置类)和不相关的ABCs注册为“虚拟子类” - 这些及其后代将被内置的 issubclass() 函数视为注册ABC的子类,但注册ABC赢得“ t显示在它们的MRO(方法解析顺序)中,注册ABC定义的方法实现也不可调用(甚至不通过 super())。 [1]

使用 ABCMeta 的元类创建的类具有以下方法:

register(subclass)

subclass 注册为此ABC的“虚拟子类”。例如:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    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的子类。如果它返回 Falsesubclass 不被认为是这个ABC的子类,即使它通常是一个。如果它返回 NotImplemented,子类检查继续使用通常的机制。

为了演示这些概念,看看这个例子ABC定义:

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class MyIterable(metaclass=ABCMeta):

    @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

最后,最后一行使得 FooMyIterable 的虚拟子类,即使它没有定义 __iter__() 方法(它使用旧式迭代协议,在 __len__()__getitem__() 定义)。请注意,这不会使 get_iterator 可用作 Foo 的方法,因此它是单独提供的。

class abc.ABC

一个帮助类,它具有 ABCMeta 作为其元类。使用这个类,可以通过简单地从 ABC 派生来创建抽象基类,从而避免有时混淆元类使用。

注意,ABC 的类型仍然是 ABCMeta,因此从 ABC 继承需要关于元类使用的通常的预防措施,因为多重继承可能导致元类冲突。

3.4 新版功能.

abc 模块还提供以下装饰器:

@abc.abstractmethod

指示抽象方法的装饰器。

使用这个装饰器需要类的元类是 ABCMeta 或派生自它。具有从 ABCMeta 派生的元类的类不能实例化,除非其所有的抽象方法和属性都被覆盖。抽象方法可以使用任何正常的“超级”调用机制来调用。 abstractmethod() 可以用于声明属性和描述符的抽象方法。

动态地将抽象方法添加到类中,或者在创建方法或类之后尝试修改抽象状态。 abstractmethod() 只影响使用常规继承导出的子类;使用ABC的 register() 方法注册的“虚拟子类”不受影响。

abstractmethod() 与其他方法描述符结合使用时,它应该作为最内层的装饰器应用,如以下使用示例所示:

class C(metaclass=ABCMeta):
    @abstractmethod
    def my_abstract_method(self, ...):
        ...
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, ...):
        ...
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(...):
        ...

    @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的内置属性相当于:

class Descriptor:
    ...
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False) for
                   f in (self._fget, self._fset, self._fdel))

注解

与Java抽象方法不同,这些抽象方法可能有一个实现。这个实现可以通过 super() 机制从覆盖它的类中调用。这可以用作使用协作多继承的框架中的超级调用的端点。

@abc.abstractclassmethod

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

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

class C(metaclass=ABCMeta):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, ...):
        ...

3.2 新版功能.

3.3 版后已移除: 现在可以使用 classmethodabstractmethod(),使这个装饰器冗余。

@abc.abstractstaticmethod

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

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

class C(metaclass=ABCMeta):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(...):
        ...

3.2 新版功能.

3.3 版后已移除: 现在可以使用 staticmethodabstractmethod(),使这个装饰器冗余。

@abc.abstractproperty(fget=None, fset=None, fdel=None, doc=None)

内置 property() 的子类,表示抽象属性。

使用这个函数需要类的元类是 ABCMeta 或派生自它。具有从 ABCMeta 派生的元类的类不能实例化,除非其所有的抽象方法和属性都被覆盖。可以使用任何正常的“超级”调用机制来调用抽象属性。

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

class C(metaclass=ABCMeta):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

上面的例子定义了一个只读属性;您还可以通过将一个或多个基础方法适当地标记为抽象来定义读写抽象属性:

class C(metaclass=ABCMeta):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

如果只有一些组件是抽象的,只有那些组件需要更新以在子类中创建一个具体的属性:

class D(C):
    @C.x.setter
    def x(self, val):
        ...

3.3 版后已移除: 现在可以使用 propertyproperty.getter()property.setter()property.deleter()abstractmethod(),使这个装饰器是多余的。

abc 模块还提供以下功能:

abc.get_cache_token()

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

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

3.4 新版功能.

脚注

[1]

C++程序员应该注意Python的虚拟基类概念与C++不一样。