дополнительная проверка mypy lambda

#python #lambda #mypy

#python #лямбда #mypy

Вопрос:

Я заметил, что mypy забывает информацию о типе при переходе в область действия лямбды. Ниже приведен небольшой пример для объяснения:

 from typing import Optional, Callable

def wrapper(x: Callable[[], None]):
    x()

def foo(a: int):
    print(str(a))

a: Optional[int] = 0

if a is None:
    exit()

wrapper(lambda: foo(a))
  

Mypy жалуется на этот фрагмент с ошибкой: Argument 1 to "foo" has incompatible type "None"; expected "int" .
Однако мы уже проверили, что a не None раньше. Это удивительно странно, потому что, если бы мы вызывали foo(a) напрямую (без оболочки), это действительно работало.
Я использую python 3.6.10 с mypy 0.740.

Это предполагаемое поведение? И если да, то как бы вы правильно это напечатали?

Спасибо!

Комментарии:

1. Ваша функция foo возвращает None — это действительно то, что вы хотите?

2. Да, в этом минимальном примере нормально foo возвращать None .

3. кстати, ваш фрагмент выполняется для меня на 3.8 без ошибок, выводя «0», также кажется, что в этом случае mypy ведет себя иначе, чем классический python

4. Ах, я должен был упомянуть в сообщении, что я использую python 3.6.10 с mypy 0.740. Я отредактирую его.

Ответ №1:

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

Как и в случае с аналогичными проблемами исправления замыканий в функциях, принудительное вычисление имени при определении обеспечивает правильную типизацию:

 wrapper(lambda a=a: foo(a))
  

Обратите внимание, что mypy не может правильно определять lambda типы, поэтому lambda подобное использование все равно приведет к ошибке Cannot infer type of lambda .


Рассмотрим полностью эквивалентную именованную функцию:

 def l() -> None:
    # reveal_type(a) -> Union[builtins.int, None]
    return foo(a)
  

Обратите внимание, как a: Optional[int] здесь видно, а не уменьшено a: int после a is None защиты. Таким образом, проблема не в from lambda вместо def .

Источник проблемы заключается в том, что оба l и lambda закрытие по всем значениям a . Даже если a: None он был отклонен перед определением l / lambda , он действителен для a последующего сброса.

 a: Optional[int]

if a is None: exit()

def l() -> None:
    # reveal_type(a) -> Union[builtins.int, None]
    return foo(a)

a = None
  

Комментарии:

1. Я попробовал это, но я получил следующую ошибку: error: Cannot infer type of lambda

2. @MatthiasVerstraete Я боюсь, что это недостаток MyPy. Я связал соответствующую проблему.

3. Хорошо, спасибо за ссылку на проблему. Это все объясняет для меня.