Абстрактные базовые классы 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 ++.