#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. Хорошо, спасибо за ссылку на проблему. Это все объясняет для меня.