Как я могу получить формат, чтобы не вызывать ошибку подсказки типа?

У меня есть следующие понимания списка в Python:

from typing import cast

# everything is fine
print([value for value in [1, 2, 3, 4]])

# on the first "value": Expression type contains "Any" (has type "List[Any]")
print("{}".format([value for value in [1, 2, 3, 4]]))

# on the "cast": Expression type contains "Any" (has type "List[Any]")
print("{}".format([cast(int, value) for value in [1, 2, 3, 4]]))

Почему использование format приводит к тому, что Mypy возвращает мне ошибки? Как видите, я пытался использовать кастинг, но все равно не получилось.

Этот вопрос выглядит похожим, но мой конкретный случай странный, потому что Mypy, кажется, в порядке, поскольку пока я не использую функцию format (но с функцией print всегда все в порядке).

Могу ли я что-нибудь сделать, чтобы строки с форматированием не выдавали мне ошибок? (Или мне просто # type: ignore их?)

РЕДАКТИРОВАТЬ: обратите внимание, что это проблема не только моего линтера Atom. Я использую Mypy версии 0.701 и запустил Mypy для файла со следующим результатом:

$ python3 -m mypy testing_list_iter.py --disallow-any-expr
testing_list_iter.py:7: error: Expression type contains "Any" (has type "List[Any]")
testing_list_iter.py:10: error: Expression type contains "Any" (has type "List[Any]")

person Pro Q    schedule 17.06.2019    source источник
comment
@AmartyaGaur Да, все три оператора правильно работают в Python. Это Mypy выдает ошибку. (Mypy не должен выдавать [1, 2, 3, 4] вывода)   -  person Pro Q    schedule 17.06.2019


Ответы (1)


На самом деле это не имеет ничего общего с пониманием списка: на самом деле это плохое взаимодействие между сигнатурой типа для str.format(...), тем, как mypy выполняет вывод типа, и флагом --disallow-any-expr.

Вот подпись типа для str.format(...), вытащенная из typeshed:

def format(self, *args: Any, **kwargs: Any) -> str: ...

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

Итак, в этом случае, поскольку все аргументы здесь Any, mypy поймет, что может сократить большую часть вывода типов, который обычно требуется делать. Итак, если мы передадим любой литерал списка в str.format(...), mypy просто решит: «Эй, выведенный тип может быть просто List[Any]».

Вот пример программы, демонстрирующей такое поведение (при проверке флагом --disallow-any-expr):

from typing import cast, Any

def test1(x: Any) -> None:
    pass

def test2(x: object) -> None:
    pass

# Revealed type is 'builtins.list[Any]'
# Expression type contains "Any" (has type "List[Any]")
test1(reveal_type([1, 2, 3, 4]))

# Revealed type is 'builtins.list[builtins.int*]'
test2(reveal_type([1, 2, 3, 4]))

Обратите внимание, что когда мы пытаемся использовать функцию, которая принимает object вместо Any, mypy выведет полный тип вместо этого сокращения. (Технически Mypy мог бы использовать такой же ярлык, поскольку все типы также являются подклассами object, но я подозреваю, что это было просто проще с точки зрения реализации - в отличие от Any, object - это просто обычный старый тип, поэтому взаимодействие с особым регистром с это как-то странно)

Обычно не имеет большого значения, как именно mypy обрабатывает этот случай: вы получите точные результаты в любом случае.

Тем не менее, флаг --disallow-any-expr все еще довольно новый и относительно непроверенный (он слишком агрессивен для многих людей, особенно для тех, кто пытается использовать mypy в существующих кодовых базах), поэтому мы время от времени сталкиваемся с этими плохими взаимодействиями.


Итак, что исправить?

Наилучшим решением для вас было бы отправить запрос на включение в Typeshed, изменяющий str.format(...) и unicode.format(...) в builtins.pyi, чтобы они принимали объекты вместо Any.

В любом случае это изменение будет соответствовать руководству по внесению вклада от Typeshed, в частности , этот фрагмент в середине раздела «Условия»:

При добавлении подсказок типа по возможности избегайте использования типа Any. Зарезервируйте использование Any, когда:

  • правильный тип не может быть выражен в текущей системе типов; и
  • чтобы избежать возврата Союза (см. выше).

Обратите внимание, что Any не является правильным типом для использования, если вы хотите указать, что некоторая функция может принимать буквально что угодно: в этих случаях вместо этого используйте object.

Затем вы ждете следующего выпуска mypy, который теоретически должен быть скоро.

Тем временем вы можете просто присвоить результаты обработки списка новой переменной, а затем передать это в str.format(...):

results = [value for value in [1, 2, 3, 4]]
print("{}".format(results))

Это приведет к тому, что mypy выведет тип вашего понимания списка без контекста Any, в результате чего он выведет полноценный тип. Это позволяет избежать плохого взаимодействия с флагом --disallow-any-expr.

person Michael0x2a    schedule 17.06.2019
comment
Запрос на извлечение сделан! (Спасибо, что вы не просто сделали это сами, а вместо этого показали мне и другим, как такие вещи исправляются — я очень ценю это!) - person Pro Q; 17.06.2019