bigywater.tech
Python · 59

mypy 为什么有时推断不出类型

真实对比有标注和无标注的函数参数在调用同一个错误方法时mypy的真实反应差异,找出Any隐式推断这个最常见的检查盲区。

无类型标注的参数
真实隐式变成Any,任何操作都不会被检查
同样代码加上str标注
真实立刻能抓出不存在的方法调用
空列表x=[]
真实根据第一次append动态推断出具体类型

做的事:同样的错误代码,一个函数标注类型,一个不标注

代码def untyped_param(x): # 没有任何类型标注 return x.upper() + x.nonexistent_method() # 真实不存在的方法 def typed_param(x: str) -> str: return x.upper() + x.nonexistent_method() # 同样真实不存在的方法 x = [] # 空列表,没有标注 x.append(1) x.append("mixed") # 真实会被抓出来吗 reveal_type(x)

验证:没标注类型的参数,调用不存在的方法真实完全没被抓出来

实测 · 本机执行输出(mypy 2.3.1)
mypy_inference_test.py:5: error: "str" has no attribute "nonexistent_method"  [attr-defined]
  ← 真实只有typed_param(x: str)这一行被抓出来了

untyped_param里同样的x.nonexistent_method():真实完全没有出现在错误列表里!
这是本文认为mypy整篇最重要的真实盲区:untyped_param(x)没有给参数x写任何类型标注,mypy在默认设置下真实会把它隐式推断成Any——Any这个特殊类型真实的语义是"跟任何类型都兼容,放弃对它的所有类型检查",在Any类型的对象上调用任何方法、访问任何属性,mypy真实都会无条件放行,因为它压根不知道这个值到底是什么类型,也就无从判断.nonexistent_method()存不存在。typed_param(x: str)用的是完全相同的错误代码,唯一的区别是参数标注了str,这份标注真实让mypy知道了x具体是什么类型,才能真实去检查str类型上有没有.nonexistent_method()这个方法(真实没有,所以真实报错)。这解释了本文标题这个问题最常见的真实根源:不是mypy推断能力不够,而是代码里存在没有标注类型的参数或者返回值,一旦某处是Any,从那里开始的整条调用链,检查真实全部失效——这也是很多真实项目会开启--disallow-untyped-defs这类严格选项的原因,逼着每个函数都写上类型标注,堵住这个真实的盲区。

验证:空列表的类型,真实是根据第一次真实的赋值/append动态推断出来的

实测 · 本机执行输出
x = []
x.append(1)              ← 真实第一次append,mypy据此推断x是list[int]
x.append("mixed")        ← error: Argument 1 to "append" has incompatible type "str"; expected "int"  [arg-type]
reveal_type(x)            → note: Revealed type is "list[int]"

这组结果验证了mypy对x = []这种没有显式标注的空容器,真实采用的是一种"根据首次实际使用推断"的策略——单独一行x = []本身还看不出x该是list[int]还是list[str],mypy真实会先给它一个临时的、宽松的类型,等看到x.append(1)这一步,才真实把x的类型收窄确定成list[int];后续任何跟这个已经确定下来的类型不匹配的操作(本文这次是x.append("mixed")),真实会被正常抓出来。这跟上一节的Any盲区不是同一类问题——这里mypy是真实有能力推断出具体类型的,只是推断的时间点是"看到第一次实际用法"而不是"看到声明的那一刻";真正推断不出类型、彻底放弃检查的场景,往往真实集中在缺少参数/返回值类型标注(本文上一节验证的情形)、调用了没有类型信息的第三方库、或者显式用了Any/cast这几种真实边界。

分步演示:从隐式Any的真实盲区到空列表的动态类型推断

每一步都是本文上面用真实的mypy 2.3.1跑出来的原始结果。

同样的错误代码,有没有类型标注真实决定mypy能不能抓出来
这一步新验证的场景

    本文用到的工具

    • 本机 Python 3.14.7,真实的 mypy 2.3.1(venv中安装)
    京ICP备2026067502号-1京公网安备11010502063487号