Документация Python неофициальный перевод

27.7. abc – Абстрактные базовые классыabc – Abstract Base Classes

Исходный код: Lib/abc.py


Этот модуль предоставляет инфраструктуру для определения абстрактных базовых классов (ABC) в Python, как описано в PEP 3119; см. PEP, чтобы понять, почему это было добавлено в Python. (См. также PEP 3141 и модуль numbers относительно иерархии типов для чисел на основе ABC.)

Модуль collections содержит несколько конкретных классов, которые наследуются от ABC; их, конечно, можно наследовать далее. Кроме того, модуль collections содержит несколько ABC, которые можно использовать для проверки, предоставляет ли класс или экземпляр определённый интерфейс, например, является ли он хешируемым или отображением.

Этот модуль предоставляет следующий класс:

class abc.ABCMeta

Метакласс для определения абстрактных базовых классов (ABC).

Use this metaclass to create an ABC. An ABC can be subclassed directly, and then acts as a mix-in class. You can also register unrelated concrete classes (even built-in classes) and unrelated ABCs as “virtual subclasses” – these and their descendants will be considered subclasses of the registering ABC by the built-in issubclass() function, but the registering ABC won’t show up in their MRO (Method Resolution Order) nor will method implementations defined by the registering ABC be callable (not even via super()). [1]

Классы, созданные с метаклассом ABCMeta, имеют следующий метод:

register(subclass)

Регистрирует подкласс как «виртуальный подкласс» этого ABC. Например:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)

Также можно переопределить этот метод в абстрактном базовом классе:

__subclasshook__(subclass)

(Должен быть определён как метод класса.)

Проверяет, считается ли subclass подклассом этого ABC. Это означает, что можно дополнительно настроить поведение issubclass без необходимости вызывать register() для каждого класса, который требуется считать подклассом ABC. (Этот метод класса вызывается из метода __subclasscheck__() ABC.)

Этот метод должен возвращать 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(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__(), определённый здесь, говорит, что любой класс, имеющий метод __iter__() в своём __dict__ (или в атрибуте __dict__ одного из своих базовых классов, доступ к которому осуществляется через список __mro__), также считается MyIterable.

Наконец, последняя строка делает Foo виртуальным подклассом MyIterable, даже если он не определяет метод __iter__() (он использует старый протокол итерации, основанный на __len__() и __getitem__()). Обратите внимание, что это не сделает get_iterator доступным как метод Foo, поэтому он предоставляется отдельно.

Он также предоставляет следующие декораторы:

@abc.abstractmethod

Декоратор, указывающий на абстрактные методы.

Использование этого декоратора требует, чтобы метакласс класса был ABCMeta или был производным от него. Класс, метакласс которого является производным от ABCMeta, не может быть инстанциирован, если не переопределены все его абстрактные методы и свойства. Абстрактные методы можно вызывать с помощью любого из обычных механизмов вызова через 'super'.

Динамическое добавление абстрактных методов в класс или попытка изменить статус абстрактности метода или класса после его создания не поддерживаются. abstractmethod() влияет только на подклассы, производные с помощью обычного наследования; «виртуальные подклассы», зарегистрированные методом ABC register(), не затрагиваются.

Использование:

class C(metaclass=ABCMeta):
    @abstractmethod
    def my_abstract_method(self, ...):
        ...

Примечание

В отличие от абстрактных методов Java, эти абстрактные методы могут иметь реализацию. Эту реализацию можно вызвать через механизм super() из класса, который его переопределяет. Это может быть полезно как конечная точка для вызова super во фреймворке, использующем кооперативное множественное наследование.

@abc.abstractclassmethod

Подкласс встроенного classmethod(), указывающий на абстрактный классовый метод. В остальном он аналогичен abstractmethod().

Использование:

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

Новое в версии 3.2.

@abc.abstractstaticmethod

Подкласс встроенного staticmethod(), указывающий на абстрактный статический метод. В остальном он аналогичен abstractmethod().

Использование:

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

Новое в версии 3.2.

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

Подкласс встроенной функции property(), обозначающий абстрактное свойство.

Использование этой функции требует, чтобы метакласс класса был ABCMeta или был производным от него. Класс, метакласс которого является производным от ABCMeta, не может быть инстанциирован, если не переопределены все его абстрактные методы и свойства. Абстрактные свойства можно вызывать с помощью любого из обычных механизмов вызова через 'super'.

Использование:

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

Это определяет свойство только для чтения; можно также определить абстрактное свойство чтения-записи, используя «длинную» форму объявления свойства:

class C(metaclass=ABCMeta):
    def getx(self): ...
    def setx(self, value): ...
    x = abstractproperty(getx, setx)

Сноски

[1]Программистам на C++ следует отметить, что концепция виртуального базового класса в Python отличается от C++.