abc — Абстрактные базовые классы

Источник: Lib/abc.py


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

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

Этот модуль предоставляет метакласс ABCMeta для определения ABC и вспомогательный класс 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

Added in version 3.4.

class abc.ABCMeta

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

Используйте этот метакласс для создания ABC. ABC может быть непосредственно подклассифицирован, и тогда он выступает в качестве смешанного класса. Вы также можете зарегистрировать несвязанные конкретные классы (даже встроенные) и несвязанные ABC как «виртуальные подклассы» - они и их потомки будут считаться подклассами регистрирующего ABC встроенной функцией issubclass(), но регистрирующий ABC не будет отображаться в их MRO (Method Resolution Order), а реализации методов, определенные регистрирующим ABC, не будут вызываться (даже через super()). [1]

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

register(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() дальше без необходимости вызывать register() для каждого класса, который вы хотите считать подклассом ABC. (Этот метод класса вызывается из метода __subclasscheck__() ABC).

Этот метод должен возвращать True, False или NotImplemented. Если он возвращает 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 стандартный метод iterable, __iter__(), определяется как абстрактный метод. Приведенная здесь реализация может быть вызвана из подклассов. Метод get_iterator() также является частью абстрактного базового класса MyIterable, но его не нужно переопределять в неабстрактных производных классах.

Метод класса __subclasshook__(), определенный здесь, гласит, что любой класс, имеющий метод __iter__() в своем __dict__ (или в методе одного из своих базовых классов, доступ к которому осуществляется через список __mro__), тоже считается MyIterable.

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

Модуль abc также предоставляет следующий декоратор:

@abc.abstractmethod

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

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

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

Когда 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

Added in version 3.2.

Не рекомендуется, начиная с версии 3.3: Теперь можно использовать classmethod с abstractmethod(), что делает этот декоратор излишним.

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

Этот особый случай устарел, поскольку декоратор classmethod() теперь правильно идентифицируется как абстрактный, когда применяется к абстрактному методу:

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

Added in version 3.2.

Не рекомендуется, начиная с версии 3.3: Теперь можно использовать staticmethod с abstractmethod(), что делает этот декоратор излишним.

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

Этот особый случай устарел, поскольку декоратор staticmethod() теперь правильно идентифицируется как абстрактный, когда применяется к абстрактному методу:

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

Не рекомендуется, начиная с версии 3.3: Теперь можно использовать property, property.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.

Added in version 3.4.

abc.update_abstractmethods(cls)

Функция для пересчета статуса абстракции абстрактного класса. Эта функция должна вызываться, если абстрактные методы класса были реализованы или изменены после его создания. Обычно эта функция должна вызываться из декоратора класса.

Возвращает cls, чтобы использовать его в качестве декоратора классов.

Если cls не является экземпляром ABCMeta, ничего не происходит.

Примечание

Эта функция предполагает, что суперклассы cls уже обновлены. Она не обновляет никакие подклассы.

Added in version 3.10.

Сноски