Spec-Zone.ru › Python 3.8

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

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

Этот модуль предоставляет инфраструктуру для определения абстрактных базовых классов (ABC) в Python, как описано в PEP 3119; см. PEP для понимания причин добавления этой возможности в Python. (См. также PEP 3141 и numbers модуль, касающийся иерархии типов чисел на основе 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 требует обычных мер предосторожности, связанных с использованием метаклассов, так как множественное наследование может привести к конфликтам метаклассов. Также можно определить абстрактный базовый класс, передав ключевое слово metaclass и используя ABCMeta напрямую, например:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

Введено в версии 3.4.

class abc.ABCMeta

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

Используйте этот метакласс для создания ABC. ABC можно наследовать напрямую, и он будет работать как класс-миксин. Вы также можете зарегистрировать несвязанные конкретные классы (даже встроенные классы) и несвязанные ABC в качестве «виртуальных подклассов» — эти подклассы и их потомки будут считаться подклассами регистрируемого ABC функцией встроенной issubclass(), но регистрируемый ABC не появится в их порядке разрешения методов (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)

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

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

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

Когда abstractmethod() применяется совместно с другими описателями методов, его следует применять как самый внутренний декоратор, как показано в следующих примерах использования:

class C(ABC):
    @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 property делает то же самое, что и:

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

Примечание

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

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

@abc.abstractclassmethod

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

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

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

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

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

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

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

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

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

class C(ABC):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(...):
        ...
@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.

Примечания

1

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

© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/abc.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API