Аннотируйте функцию Python, чтобы она сужала возвращаемый тип объединения, включающего переменную типа.Python

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Аннотируйте функцию Python, чтобы она сужала возвращаемый тип объединения, включающего переменную типа.

Сообщение Anonymous »

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

Код: Выделить всё

def g(x, y): return x, y
Предположим, что эта функция принимает два параметра одного и того же типа (TL;DR: любая пара объектов одного и того же типа, реализующая определенный мной протокол Comparable, но давайте рассмотрим подклассы int для этого примера) OR специальный строковый литерал (в этом примере давайте просто рассмотрим str). Подводя итог, эти вызовы допустимы: g(1, 2), g('hi', 'world'), g(1, 'world') и т. д., но эти вызовы не такие: g({}, 3), g([], True) и т. д.
Я ищу способ чтобы аннотировать эту функцию, чтобы mypy мог точно определить тип возвращаемого значения. Например, я ожидаю, что g(1,2) приведет к кортежу целых чисел, g('hi', 'world') к кортежу str, g (1, 'world') в tuple[int, str], а недопустимые регистры приведут к ошибке.
Я наивно написал:< /p>

Код: Выделить всё

from typing import TypeVar

T = TypeVar('T', bound=int)

def g(x: T | str, y: T | str) -> tuple[T | str, T | str]:
return x, y
AFAIK, этого должно быть достаточно, чтобы гарантировать, что оба параметра имеют один и тот же тип (и являются экземплярами int) ИЛИ что один (или оба) из них являются str.
Однако, когда я выполняю mypy в этом фрагменте следующим образом:

Код: Выделить всё

reveal_type(g(3, 4))
reveal_type(g('hello', 'world'))
reveal_type(g('hello', 2))
Я получил такой вывод:

Код: Выделить всё

main.py:8: note: Revealed type is "tuple[Union[builtins.int, builtins.str], Union[builtins.int, builtins.str]]"
main.py:9: note: Revealed type is "tuple[builtins.str, builtins.str]"
main.py:10: note: Revealed type is "tuple[Union[builtins.int, builtins.str], Union[builtins.int, builtins.str]]"
Success: no issues found in 1 source file
Вы можете увидеть это вживую на mypy Playground: https://mypy-play.net/?mypy=latest&pyth ... e3376deb5c
Вторая строка — это то, что я ожидал (кортеж строк), но два других вызова должны (или могут) быть уточнены до tuple[int, int] и кортеж[str, int]. Результат аналогичен PyRight и просто аналогичен Pyre (последний указывает tuple[str, Literal[2]] вместо tuple[str, int] в последнем случае по какой-то неизвестной причине ).
Это что-то выходит за рамки модуля ввода или за пределами mypy? Я что-то упустил?
Обратите внимание, что я не могу написать T = TypeVar('T', int, str). Хотя это работает (для приведенного выше примера), помните, что я использовал int для упрощения случая, когда на практике я хочу, чтобы T был любым классом, реализующим мой протокол Comparable (поэтому мне нужно чтобы определить T с верхней границей, а не со списком точных типов, это T = TypeVar('T',bound=Comparable)).
< hr />
Чтобы добавить дополнительный контекст для этого вопроса (не читайте, если вам все равно ;-), библиотека, которую я хочу аннотировать, определяет класс Interval, состоящий из двух границ (нижний и верхний). Этими границами может быть любой объект, поддерживающий сравнение (например, целые числа, числа с плавающей запятой, даты). Для обработки бесконечных и полубесконечных интервалов библиотека определяет два специальных объекта, а именно inf и -inf, которые являются соответственно одноэлементными экземплярами _PInf и _NInf. Моя цель — аннотировать этот класс Interval, чтобы mypy мог проверить, является ли Interval(x, y) допустимым (т. е. x и y имеют один и тот же Comparable< Тип /code> или x или y или оба являются экземплярами _PInf _NInf).
Обобщая приведенный выше пример, Сейчас у меня есть что-то вроде этого:

Код: Выделить всё

Comparable = ...  # Protocol with __eq__, __lt__, __le__, ...
T = TypeVar('T', bound=Comparable)
Bound: TypeAlias = T | _PInf | _NInf

class Interval(Generic[T]):
def __init__(self, lower: Bound[T], upper: Bound[T]) -> None:
...
Цель состоит в том, чтобы (1) убедиться, что обе границы «совместимы» (как объяснено в предыдущем абзаце), и (2) чтобы, например, Interval(1, 2) .lower + 1 не вызывает никаких жалоб со стороны mypy, а Interval(-inf, 2).lower + 1 вызывает.


Подробнее здесь: https://stackoverflow.com/questions/780 ... an-union-i
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Python»