Mypy在Lambda作用域中丢失类型信息的技术咨询
这确实是mypy的预期行为,原因及解决方案如下
为什么会出现这个错误?
mypy的静态类型分析没办法追踪lambda这种延迟执行代码块里的变量类型变化。虽然你在定义lambda之前已经通过if a is None: exit()排除了a为None的情况,但lambda是在wrapper函数内部才会被调用——mypy无法保证到调用时a的类型依然是int(静态分析会考虑后续代码修改a的可能性,哪怕你的示例里没有这种操作)。而直接调用foo(a)时,mypy能确定当前上下文里a的类型已经被缩小为int,所以不会报错。
解决方法
这里有几种靠谱的方式来让mypy正确识别类型:
1. 用局部变量捕获当前值
把已经类型缩小后的a赋值给一个局部变量,lambda引用这个局部变量。因为局部变量在当前上下文里的类型是确定的,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() local_a = a # mypy确认local_a的类型是int wrapper(lambda: foo(local_a))
2. 使用functools.partial绑定参数
用partial把a作为参数预先绑定到foo上,这样mypy能明确绑定的参数类型符合foo的要求:
from typing import Optional, Callable from functools import partial def wrapper(x: Callable[[], None]): x() def foo(a: int): print(str(a)) a: Optional[int] = 0 if a is None: exit() wrapper(partial(foo, a))
3. 类型断言(谨慎使用)
如果能百分百确定运行时a不会是None,可以用assert或者cast来强制缩小类型:
from typing import Optional, Callable, cast def wrapper(x: Callable[[], None]): x() def foo(a: int): print(str(a)) a: Optional[int] = 0 assert a is not None # mypy会将a的类型缩小为int # 或者用cast:a = cast(int, a) wrapper(lambda: foo(a))
注意:assert在运行时如果条件不成立会抛出错误,而cast只是给mypy的提示,运行时不会做检查,所以优先用assert更安全。
内容的提问来源于stack exchange,提问作者Matthias Verstraete
相关产品推荐
相关产品推荐

