Python 18:类型提示能发现什么错误
在源码里写 service.submit("word_count", 42),Pyright 1.1.404 指出第二个参数是整数,预期是字符串;把相同数字藏在 json.loads 的返回值中,静态检查却没有报错,真正执行时由原来的 submit 抛出 TypeError: text must be a string。不能把“类型检查通过”写成“输入已经安全”。两次判断发生在不同的边界。
第17 篇的 CLI对解码和输入长度的检查仍保留。本篇加一层类型明确的任务接口,并不重写任务引擎或重新实现校验。示例仍使用 tasklab 的进程内 store,提交、执行、查询、取消遵守原状态契约。
接口是可检查的承诺,不是运行时门卫
examples/python-language/src/tasklab/typed.py 定义 TaskGateway(Protocol),列出四个操作及其参数和返回值;MemoryTaskGateway 把这些操作委托给已经存在的 app。变量标为 TaskGateway 时,检查器只需看方法的形状,不必让任务对象继承这个类。这是 Python 3.12 的结构化协议用途。内存实现仍会调用 app.submit 的长度、种类、字符串检查;类型声明自身不会在 service.submit(...) 调用时拦截参数。
正例把 MemoryTaskGateway 赋给 TaskGateway,提交 "One two one",执行后查询成功状态,再在测试中提交另一任务并取消。Pyright 对正例及接口实现报告 0 errors;真实 Python 执行获得 task-0001 和 {"one": 2, "two": 1}。负例只在 experiments/18_static_negative.py 中写死整数参数,并由检查器报告 reportArgumentType、退出 1;不能直接执行这个负例然后把 Python 抛异常冒充静态检查结果。
1 | |
本次 --pythonversion 3.12 与运行用的 /usr/bin/python3.12 对齐,但静态检查的目标版本并不会选择运行进程、也不会执行 submit。npx 在仓库外取得检查器;pyproject.toml 没有因此新增运行依赖,也尚无完整解析锁。Pyright tarball 的 npm registry integrity 值和实际版本记录在本篇实验环境中,不把依赖范围当作完整锁结果。
Any 从哪里漏进来
json.loads('{"kind":"word_count","text":42}') 返回动态解析的结构。检查器无法仅从这个调用确定 text 必为字符串,Any 可流经已标注的调用参数;正例的静态检查通过不等于传入值可信。本次实际调用 MemoryTaskGateway.submit 后由 app.submit 拒绝,任务字典仍为空。把类型提示当解析器,会让不可信输入绕开错误边界。
这并不是劝人放弃类型标注。明确方法参数能在开发阶段挡住写死在调用点的拼写或形状错误;运行时还需按输入来源分别验证字段类型、长度和内容。20 才引入版本化 JSON schema,届时要在 JSON 边界做字段级拒绝,不能仅靠 cast 宣称解析结果正确。TaskGateway.query 目前返回 dict[str, object],不足以让检查器证明快照中 state 和 result 的所有关系;也没有把整个未标注的旧应用宣称为静态无误。
预测题
只把 service.submit("word_count", 42) 改成 payload = json.loads('{"kind":"word_count","text":42}') 再传 payload["text"],静态负例还一定会报错吗?任务会被创建吗?
答案:本次 Pyright 对后者的正例没有报错;运行时 submit 检查 isinstance(text, str) 抛出 TypeError,在分配 ID 之前退出,任务仍为空。实验分别记录静态输出和运行输出;不同检查器或严格配置可能对 Any 使用另给诊断,不据此推断所有工具永远沉默。
变更题
删掉 app.submit 的字符串检查,只保留 TaskGateway.submit(kind: str, text: str)。哪项证据会立即失效?
答案:从 JSON 取得整数并直接传入的运行时拒绝断言会失败,调用还可能在 text.encode 处抛出不匹配接口的 AttributeError;检查器的正例结果不能替换该断言。要继续防住无效外部输入,保留边界校验并检查未创建任务;这是可以实际破坏/恢复代码验证的变式,本章没有删去检查并运行此变式。
下一篇追踪导入、模块缓存和 python -m 启动时哪些语句会执行。系列导航只列已验收的正文。





