Пытаясь создать рекурсивные типы для аннотирования вложенной структуры данных, я наткнулся на следующее.
Этот код верен согласно mypy:
IntType = int | list["IntType"] | tuple["IntType", ...]
StrType = str | list["StrType"] | tuple["StrType", ...]
def int2str(x: IntType) -> StrType:
if isinstance(x, list):
return list(int2str(v) for v in x)
if isinstance(x, tuple):
return tuple(int2str(v) for v in x)
return str(x)
Но не этот, который должен быть эквивалентен:
IntType = int | list["IntType"] | tuple["IntType", ...]
StrType = str | list["StrType"] | tuple["StrType", ...]
def bad_int2str(x: IntType) -> StrType:
if isinstance(x, (list, tuple)):
return type(x)(bad_int2str(v) for v in x) # error here
return str(x)
Сообщение об ошибке
line 6: error: Incompatible return value type (
got "list[int | list[IntType] | tuple[IntType, ...]] | tuple[int | list[IntType] | tuple[IntType, ...], ...]",
expected "str | list[StrType] | tuple[StrType, ...]"
) [return-value]
line 6: error: Generator has incompatible item type
"str | list[StrType] | tuple[StrType, ...]";
expected "int | list[IntType] | tuple[IntType, ...]" [misc]
Я предполагаю, что mypy может сделать вывод, что type(x) — это либо list, либо tuple.
Это ограничение mypy или в этом коде есть что-то подозрительное?
Если да, то откуда ограничение?






Mypy — это средство проверки статического типа, что означает, что он не оценивает type(x)(bad_int2str(v) for v in x) при запуске. Таким образом, Mypy не может знать, что тип выражения — это StrType, при текущих ограничениях реализации. (Mypy считает, что это выражение list[IntType] | tuple[IntType, ...]. См. Ограничения.)
Вы можете дать подсказку на type(x), например, в следующих примерах.
def good_int2str(x: IntType) -> StrType:
if isinstance(x, (list, tuple)):
ty: type[list | tuple] = type(x)
return ty(good_int2str(v) for v in x)
return str(x)
def another_good_int2str(x: IntType) -> StrType:
if isinstance(x, (list, tuple)):
return cast(type[list | tuple], type(x))(
another_good_int2str(v) for v in x)
return str(x)
Нет, мое описание не неверно. Mypy определяет правильный тип аргумента генератора, но не для всего выражения вызова конструктора.
Он тоже знает, что (list[<IntType>] | tuple[<IntType>, ...], хотя и не так, как ожидал ОП, С.Терляков объяснил почему), отсюда и ошибка «несовместимый тип возвращаемого значения».
Нет, это не так. Проверьте, что написано в ссылке: «Анализ Mypy ограничивается отдельными символами и не отслеживает взаимосвязи между символами».
Я не думаю, что это имеет какое-либо отношение к рассматриваемому вопросу. Вам нужно будет уточнить: какие «отдельные символы» и какие «отношения» не отслеживает Mypy?
Я имею в виду, что Mypy не может вывести конечный результат c(a) равно StrType, когда тип a — Generator[StrType, None, None], а тип c — type[list[<IntType>]] | type[tuple[<IntType>, ...]]. В этом примере используются два символа: a и c.
... что возвращает нас к ответу С.Т. Терлякова, и нет, раздел «Ограничения» не посвящен отношениям между вызывающими и вызываемыми объектами, такими как ваши a и c. То, что Mypy не может вывести результат с помощью OP, не означает, что он «не может» узнать. Если бы этого не было, тип был бы Any.
Для type(x) не существует стирания типа.
Что должен сказать mypy о следующем?
x: list[int] = [1]
reveal_type(type(x))
Если мы спросим, там будет сказано:
Выявленный тип: «type[builtins.list[builtins.int]]».
Итак, когда вы запрашиваете type(x)(some_strtype_iterator), он справедливо жалуется, что вы пытаетесь создать list[StrType] | tuple[StrType, ...] из итерации IntType.
Обе ошибки намекают на этот факт: mypy думает, что вы возвращаете список или кортеж IntType, поскольку ошибка внутри выражения не приводит к потере уже известного типа. И это также указывает на то, что вы не можете построить список/кортеж IntType из генератора, дающего StrType - вы не собирались строить последовательность IntType, но type(x) требует этого.
Я только что начал обсуждение на типографском форуме, чтобы выяснить причины, по которым не удаляются дженерики для type(x).
Интересный. Оглядываясь назад, это имеет смысл. Есть ли еще где-нибудь задокументированные подобные «конфликты» между проверкой статического типа и поведением во время выполнения?
@Winks В этом случае поведение зависит от реализации. Pytype, например, передает ваш bad_int2str(). Подобные случаи можно найти в различных обсуждениях, например, в вопросах GitHub, но полной документации пока нет (даже спецификаций).
«[...] Mypy не знает, что тип выражения — это
StrType»: Неверно; он знает (reveal_type(bad_int2str(v) for v in x)показываетGenerator[<StrType>, None, None]), поэтому в первую очередь выдает ошибку «генератор имеет несовместимый тип элемента».