mypy lambda 可选检查

mypy lambda optional check

我注意到 mypy 在进入 lambda 范围内时忘记了类型信息。下面举个小例子来说明:

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。

这是有意为之的行为吗?如果是这样,您将如何正确输入?

谢谢!

这不是因为 lambda,而是因为 a 是闭包。简而言之,即使特定代码无法触发错误路径,该函数在技术上 也是错误的。

与修复函数中的闭包的类似问题一样,在定义时强制对名称求值可提供有效的类型:

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

注意mypy cannot properly infer lambda types,所以这样使用lambda还是会报错Cannot infer type of lambda


考虑完全等效的命名函数:

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

注意 a: Optional[int] 在这里是如何可见的,而不是 a is None 守卫之后的缩小的 a: int。所以问题不是来自 lambda 而不是 def.

问题的根源在于 llambda 都关闭了 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