Абстрактные базовые классы abc
¶
Исходный код: Lib/abc.py
Данный модуль предоставляет инфраструктуру для определения
абстрактные базовые классы (ABC) в Python’е, как описано в PEP 3119;
для получения информации о том, зачем он был добавлен в Python, см. PEP.
(См. также PEP 3141 и numbers
модуль, относящийся к иерархии типов
чисел на основе ABCs.)
Модуль collections
содержит некоторые классы, вытекающие из ABC;
они, конечно, могут быть получены в дальнейшем. Кроме того, подмодуль collections.abc
содержит некоторые ABCs, для проверки того, что класс или сущность предоставляет
определенный интерфейс, например, реализованы ли возможностьи хэширования или если они
отображение.
Модуль предоставляет метакласс ABCMeta
для определения ABCs
и вспомогательный класс ABC
для альтернативного определения ABCs посредством наследования:
-
class
abc.
ABC
¶ Вспомогательный класс, который использует
ABCMeta
в качестве своего метакласса. С этим классом, абстрактный базовый класс может быть создан производным от :class: ABC избегая иногда запутанного использования метакласса, например:from abc import ABC class MyABC(ABC): pass
Следует отметить, что тип
ABC
все еще являетсяABCMeta
, поэтому наследование отABC
требует обычных мер предосторожности в отношении использования метакласса, так как множественное наследование может привести к метакласс конфликтам. Можно также определить абстрактный базовый класс, передавая ключевое слово метакласса и используя непосредственноABCMeta
, например:from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass .. versionadded:: 3.4
-
class
abc.
ABCMeta
¶ Метаклассы для определения абстрактных базовых классов.
Использование метакласса применяется для создания ABC. ABC может быть подклассами непосредственно, а затем выполняться как класс миксин. Также можно зарегистрировать несвязанные конкретные классы (даже встроенные классы) и несвязанные ABC как «виртуальный подклассы» - их и их потомков будет считать подклассами регистрирующими встроенной ABC функцией
issubclass()
, но регистрирующаяся ABC не обнаружится в их 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)¶ (Должен быть определен как метод класса.)
Проверьте, является ли субкласс субклассом 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()
влияет только на подклассы, полученные с помощью регулярного наследования; «Виртуальные подклассы», зарегистрированные методомregister()
ABC, не затрагиваются.Когда
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, абстрактные методы могут иметь реализацию. Эта реализация может быть вызывается через механизм: func: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()
, указывающего на абстрактный 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 ++. |