Python. Не понимаю почему Generic абстрактный базовый класс?

Рейтинг: 0Ответов: 1Опубликовано: 05.08.2023

Из того, что прочитал в статьях про абстракцию. Что абстрактные классы - это такие классы, в которых находятся один или несколько абстрактных методов. Для этого импортируется из модуля abc класс ABC. В документации написано , что Generic это абстрактный базовый класс для создания универсальных типов. (Код класса Generic из документации в самом низу) Но почему в нём тогда нету абстрактных методов с декоратором @abstractmethod или без (который без реализации автоматически приписывается к абстрактному). Почему он не наследует ABC класс? Внутри подробна реализация методов присутствует, что тоже говорит о том что методы не абстрактные. Почему тогда называют абстрактным его?

class Generic:
    """Abstract base class for generic types.

    A generic type is typically declared by inheriting from
    this class parameterized with one or more type variables.
    For example, a generic mapping type might be defined as::

      class Mapping(Generic[KT, VT]):
          def __getitem__(self, key: KT) -> VT:
              ...
          # Etc.

    This class can then be used as follows::

      def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
          try:
              return mapping[key]
          except KeyError:
              return default
    """
    __slots__ = ()
    _is_protocol = False

    @_tp_cache
    def __class_getitem__(cls, params):
        if not isinstance(params, tuple):
            params = (params,)
        if not params and cls is not Tuple:
            raise TypeError(
                f"Parameter list to {cls.__qualname__}[...] cannot be empty")
        params = tuple(_type_convert(p) for p in params)
        if cls in (Generic, Protocol):
            # Generic and Protocol can only be subscripted with unique type variables.
            if not all(isinstance(p, (TypeVar, ParamSpec)) for p in params):
                raise TypeError(
                    f"Parameters to {cls.__name__}[...] must all be type variables "
                    f"or parameter specification variables.")
            if len(set(params)) != len(params):
                raise TypeError(
                    f"Parameters to {cls.__name__}[...] must all be unique")
        else:
            # Subscripting a regular Generic subclass.
            if any(isinstance(t, ParamSpec) for t in cls.__parameters__):
                params = _prepare_paramspec_params(cls, params)
            else:
                _check_generic(cls, params, len(cls.__parameters__))
        return _GenericAlias(cls, params,
                             _typevar_types=(TypeVar, ParamSpec),
                             _paramspec_tvars=True)

    def __init_subclass__(cls, *args, **kwargs):
        super().__init_subclass__(*args, **kwargs)
        tvars = []
        if '__orig_bases__' in cls.__dict__:
            error = Generic in cls.__orig_bases__
        else:
            error = Generic in cls.__bases__ and cls.__name__ != 'Protocol'
        if error:
            raise TypeError("Cannot inherit from plain Generic")
        if '__orig_bases__' in cls.__dict__:
            tvars = _collect_type_vars(cls.__orig_bases__, (TypeVar, ParamSpec))
            # Look for Generic[T1, ..., Tn].
            # If found, tvars must be a subset of it.
            # If not found, tvars is it.
            # Also check for and reject plain Generic,
            # and reject multiple Generic[...].
            gvars = None
            for base in cls.__orig_bases__:
                if (isinstance(base, _GenericAlias) and
                        base.__origin__ is Generic):
                    if gvars is not None:
                        raise TypeError(
                            "Cannot inherit from Generic[...] multiple types.")
                    gvars = base.__parameters__
            if gvars is not None:
                tvarset = set(tvars)
                gvarset = set(gvars)
                if not tvarset <= gvarset:
                    s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
                    s_args = ', '.join(str(g) for g in gvars)
                    raise TypeError(f"Some type variables ({s_vars}) are"
                                    f" not listed in Generic[{s_args}]")
                tvars = gvars
        cls.__parameters__ = tuple(tvars)

Ответы

▲ 0Принят

В модуле typing класс Generic является конкретным классом, который используется для создания универсальных типов. Он не наследуется от ABC класса и не содержит абстрактных методов.

Вместо этого, класс Generic предоставляет метод __class_getitem__, который используется для параметризации типов. Когда вы пишете, например, List[int], этот класс getitem будет вызываться для класса List и параметра int, чтобы создать новый тип Listint. Это позволяет вам создавать свои собственные универсальные типы, которые могут работать с разными типами данных.

Класс Generic также используется в других классах модуля typing, таких как Optional, Union и других, для указания параметров типов.

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