abc
— Абстрактные базовые классы¶
Данный модуль предоставляет инфраструктуру для определения
абстрактных базовых классов (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
требует обычных мер предосторожности в отношении использования метакласса, поскольку множественное наследование может привести к конфликтам метаклассов. Можно также определить абстрактный базовый класс, например, передав ключевой аргумент метакласса и напрямую используяABCMeta
:from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass
Добавлено в версии 3.4.
-
class
abc.
ABCMeta
¶ Метакласс для определения абстрактных базовых классов (ABC).
Используйте данный метакласс для создания ABC. ABC может быть непосредственно подклассифицирован, а затем действовать как смешанный класс. Вы также можете зарегистрировать несвязанные конкретные классы (даже встроенные классы) и несвязанные ABC как «виртуальные подклассы» — они и их потомки будут считаться подклассами регистрирующего ABC встроенной функцией
issubclass()
, но регистрирующая ABC победила. t отображаются в их MRO (Порядок разрешения методов), и при этом реализации методов, определенные регистрирующим ABC, не будут вызываемыми (даже черезsuper()
). [1]У классов, созданных с помощью метакласса
ABCMeta
, есть следующий метод:-
register
(subclass)¶ Зарегистрировать 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
, 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(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()
влияет только на подклассы, производные с использованием обычного наследования; «виртуальные подклассы», зарегистрированные с помощью метода ABCregister()
, не затрагиваются.Когда
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
, если какой-либо из методов, используемых для создания дескриптора, является абстрактным. Например, встроенный в Pythonproperty
делает эквивалент: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, ...): ...
-
@
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++. |