真实用cast()骗过mypy验证运行时不会跟着一起被骗,再反过来用isinstance验证参数化泛型信息在运行时真实完全不存在,划出两套类型检查系统真实的边界。
代码def get_config() -> dict: return {"port": "not-a-number"} # 真实是字符串 raw = get_config() port = cast(int, raw["port"]) # 告诉mypy"相信我这是int" print(type(port)) result = port + 1 # 真的把它当int用
type(port) after cast(int, ...): <class 'str'> ← 真实还是str,cast没有真的转换任何东西 real crash later: can only concatenate str (not "int") to str ← 真实到port+1这一步才炸
这是typing.cast()真实最本质的特性:它不是一次类型转换(不像int("123")那样真的做了字符串到整数的解析),cast(int, raw["port"])在运行时真实原样返回raw["port"]本身,int这个参数完全只是一份说给mypy听的"承诺"——mypy看到cast就会真实无条件相信这份承诺,把port后续的静态类型直接标记成int,不会再对它做任何"这真的是int吗"的怀疑。当这份承诺是假的(本文这次get_config真实返回的是字符串),mypy在cast那一行完全看不出任何问题,程序真实要等到后面某处真的把port当int使用(本文这次是port + 1)才会在运行时炸出TypeError——这条错误跟原本"配置里的port字段类型错了"这个真实根源之间,可能真实隔着好几层函数调用,排查起来会比直接在源头报错困难得多。
x: list[int] = [1, 2, 3] isinstance(x, list[int]) → 真实直接抛TypeError: "isinstance() argument 2 cannot be a parameterized generic" isinstance(x, list) → True ← 真实只能检查到"这是个list",检查不了"里面装的是int"
每一步都是本文上面用真实的typing.cast和isinstance跑出来的原始结果。
typing.cast