Типизация Python: универсальный тип, имеющий тот же интерфейс, что и тип-оболочка

Я хотел бы определить своего рода универсальный тип «обертки», скажем MyType[T], чтобы он имел тот же интерфейс типа, что и обернутый тип.

from typing import Generic, TypeVar

T = TypeVar("T")

class MyType(Generic):
    pass  # what to write here?

Итак, например, когда у меня есть тип MyType[int], средство проверки типов должно обрабатывать его так, как если бы это был тип int.

Это возможно? Если да, то как?

NewType возможно?
joel 05.10.2023 22:29
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
1
136
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Возможно, мне не хватает сути того, что вы ищете. Если да, то я рад пересмотреть этот ответ, но если вы хотите, чтобы новый тип проверялся так же, как и его базовый тип, подумайте:

MyType = int

a: MyType = 1

Запуск b = a + 1 не приводит к ошибкам линтинга, но c = a + "a" должен выдать предупреждение. Что-то вроде: Operator "+" not supported for types "Literal[1]" and "Literal['a']"

Я не уверен, что нужно создавать совершенно новый универсальный класс, если вы хотите, чтобы новый тип вел себя как int (в этом примере).

Это хороший ответ на другой вопрос. :)

Jonathan Scholbach 05.10.2023 23:06

Я не хочу представлять это как ответ, просто как предположение (пожалуйста, прокомментируйте, если я вас неправильно понял):

from typing import TypeVar, Generic

T = TypeVar("T")

class MyType(Generic[T]):
    def __init__(self, value: T):
        self.value = value

# Example usage
x: MyType[int] = MyType(42)
y: MyType[str] = MyType("Hello")

# The type checker will treat x.value as an int and y.value as a str
reveal_type(x.value)  # note: Revealed type is "builtins.int"
reveal_type(y.value)  # note: Revealed type is "builtins.str"

Ответ принят как подходящий

Для подтверждения вы хотите, чтобы выражение MyType[T] означало для средства проверки статического типа «подкласс MyType и T», так что объявление

class MyType:
    attr: object

приведет к следующему (например, используя mypy и int.conjugate в качестве примера):

>>> reveal_type(MyType[int].conjugate)  # def (self: builtins.int) -> builtins.int
>>> obj: MyType[int] = MyType[int]()
>>> reveal_type(obj.attr)  # builtins.object

Нет, эта идиома не поддерживается при типизации Python. Вместо этого ваш вариант использования должен быть охвачен будущей реализацией типов пересечений с предложенным синтаксисом MyType & T.

Для средства проверки статического типа синтаксис MyType[T] означает и означает только то, что MyType является универсальным типом (включая универсальные структурные типы), а API, который имеет MyType, получен из любых метаклассов, любых базовых классов и чего угодно. заявлено под телом class MyType.

  • Любая параметризация типа (например, передача int в T в MyType[T]) влияет только на общие типы в базах классов и операторы в теле класса;

  • В теле класса нет ничего, что могло бы сообщить средству проверки статического типа, что класс наследует API от другого класса, поскольку эта информация уже предоставлена ​​метаклассом и базовым классом(ами);

  • <type>[T] правильно определен как статически выводимый тип только тогда, когда <type> является определяемым пользователем универсальным типом. Статические типы, выведенные из других произвольных попыток создать допустимое выражение, нестабильны и определяются реализацией, независимо от реализации во время выполнения.

    from typing import *
    
    T = TypeVar("T")
    
    class M(type):
        def __getitem__(cls, type_: T, /) -> T: ...
    
    class A(metaclass=M): ...
    class B:
        def __class_getitem__(cls, type_: T, /) -> T: ...
    class C(Generic[T]): ...
    
    >>> A_int: TypeAlias = A[int]  # mypy: "A" expects no type arguments, but 1 given  # pyright: Expected no type arguments for class "A"
    >>> B_int: TypeAlias = B[int]  # mypy: "B" expects no type arguments, but 1 given  # pyright: <no messages>
    >>> C_int: TypeAlias = C[int]
    >>>
    >>> reveal_type(A[int])  # mypy: <overloads of `int.__new__`>                           pyright: Type of "A[int]" is "Type[int]"
    >>> reveal_type(B[int])  # mypy: The type "type[B]" is not generic and not indexable    pyright: Type of "B[int]" is "Type[int]"
    >>> reveal_type(C[int])  # mypy: Revealed type is "def () -> __main__.C[builtins.int]"  pyright: Type of "C[int]" is "Type[C[int]]"
    

Другие вопросы по теме