You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

针对字面量列表的包含测试无法让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 15:52:39