针对字面量列表的包含测试无法让mypy缩小值类型的疑问
问题:mypy为何不识别
in语句的字面量类型收窄? 最小可复现代码
from typing import Literal def expects_literal(x: Literal["foo", "bar"]) -> None: print(f"{x=}") def fails_mypy_check(y: str) -> None: if y in ["foo", "bar"]: expects_literal(y) # mypy报错位置 else: print("format incorrect")
mypy报错信息
mwe.py:8: error: Argument 1 to "expects_literal" has incompatible type "str"; expected "Literal['foo', 'bar']" [arg-type]
明明第7行的if y in ["foo", "bar"]已经确保y是允许的字面量值,为何mypy仍报错?文档提到的穷尽性检查应该支持这类场景,哪里遗漏了?
解答
问题出在mypy默认不会对列表的成员检查做类型收窄,因为列表是可变对象——mypy无法保证这个列表在执行in检查之后、调用expects_literal之前,不会被其他代码修改(比如多线程场景)。因此它无法安全地将y的类型从str收窄为Literal["foo", "bar"]。
几种可行的解决方案:
- 改用不可变元组:把守卫条件里的列表换成元组,元组是不可变类型,mypy能明确识别成员固定,从而正确收窄类型:
def works_mypy_check(y: str) -> None: if y in ("foo", "bar"): # 元组替换列表 expects_literal(y) else: print("format incorrect") - 使用类型断言(不推荐,除非确定逻辑安全):直接告诉mypy当前y的类型,但这会跳过类型检查,可能引入隐患:
if y in ["foo", "bar"]: expects_literal(Literal["foo", "bar"](y)) - 启用
--strict-equality选项:运行mypy时加上该参数,它会让mypy对字面量列表的成员检查进行类型收窄,但要注意这个选项可能对其他可变对象的相等性检查产生影响,需谨慎使用。
另外补充:你提到的“穷尽性检查”通常指match语句或if-elif链覆盖所有字面量分支的场景,和in列表的类型收窄属于不同的类型推断逻辑,mypy对这两种场景的处理规则不一样。
内容的提问来源于stack exchange,提问作者scnlf
相关产品推荐
相关产品推荐

