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

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


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

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

Этот модуль предоставляет метакласс ABCMeta для определения азбуки и вспомогательный класс 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 может быть непосредственно разделен на подклассы, а затем выступать в качестве объединенного класса. Вы также можете зарегистрировать несвязанные конкретные классы (даже встроенные классы) и несвязанные ABC как «виртуальные подклассы» - они и их потомки будут считаться подклассами регистрирующего ABC с помощью встроенной функции issubclass(), но регистрирующий ABC не будет отображаться в их MRO (Порядок разрешения метода), а также реализации методов, определенные регистрирующим 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 определяет стандартный повторяющийся метод __iter__() как абстрактный метод. Приведенная здесь реализация все еще может быть вызвана из подклассов. Метод get_iterator() также является частью абстрактного базового класса MyIterable, но его не обязательно переопределять в неабстрактных производных классах.

Метод класса __subclasshook__(), определенный здесь, гласит, что любой класс, у которого есть метод __iter__() в своем методе __dict__ (или в методе одного из его базовых классов, доступ к которому осуществляется через список __mro__), является считается, что это тоже 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: Теперь можно использовать classmethod с abstractmethod(), что делает этот декоратор излишним.

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

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

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

Добавлено в версии 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.

Добавлено в версии 3.4.

abc.update_abstractmethods(cls)

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

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

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

Примечание

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

Добавлено в версии 3.10.

Сноски

Вернуться на верх