代码语言

知识点思维导图

25 个知识节点

Python(11) - 类型注解

读完后,你应能完成以下任务:

  • 绘制“Python(11) - 类型注解 / 先给锚点:和 TS 的类型标注几乎一一对应”的关键对象与数据流,解释“唯一要适应的小差异:返回值用 ->(箭头),不是冒号。”,并用源码位置、日志或 Trace 标注证据。
  • 为“Python(11) - 类型注解 / 边界一(最重要):注解默认不检查,写错也照样跑”设计正常与异常输入,验证“这是前端从 TS 过来必踩的第一个认知坑。”,输出首个偏差位置与回归测试结果。
  • 实现“Python(11) - 类型注解 / 容器、可选、联合:写法对照”的最小代码或配置,检验“别写成 -> null,Python 里空值的类型名是 None(首字母大写)。”,输出命令、结果与 Diff,并说明不适用边界。

你在 TS 里享受过类型带来的红利:IDE 自动补全、改名安全、参数写错当场标红。Python 也有同款能力,叫 type hint(类型注解)。但它有一个让前端最容易误判的点——默认根本不检查、不报错、也不影响运行。本篇帮你把 TS 心智模型平移过来,并立刻划清那条最关键的边界:Python 的类型注解是"渐进的、给人和工具看的提示",不是 TS 那种会拦住你的强制约束。

一、先给锚点:和 TS 的类型标注几乎一一对应

TS 里你写 name: string,Python 写 name: str,位置、冒号、含义都一样:

// TS
function greet(name: string, age: number): string {
  return `${name} is ${age}`
}

let count: number = 0           // 变量注解
const users: string[] = []      // 数组

对照表先建立直觉:

TS Python 说明
name: string name: str 参数 / 变量注解,位置一致
: number : int / : float Python 整数浮点分开
: boolean : bool 布尔
): string { ) -> str: 返回值,TS 用 :,Python 用 ->
string[] list[str] 数组 / 列表
Record<string, number> dict[str, int] 键值对
string | null str | None 联合 / 可空
interface / type TypedDict / dataclass / Pydantic 结构化类型(见后文)

唯一要适应的小差异:返回值用 ->(箭头),不是冒号。其余的"在标识符后加冒号写类型"和 TS 一模一样。


二、边界一(最重要):注解默认不检查,写错也照样跑

这是前端从 TS 过来必踩的第一个认知坑。TS 的类型是编译期强约束——类型不对,tsc 直接编译失败、红线拦你。Python 的类型注解默认只是元数据:解释器读到它、存起来,但不验证、不报错、不影响运行

// TS 对比:这行根本编译不过
function greet(name: string): string { return `hi ${name}` }
greet(123)              // ❌ 编译错误:Argument of type 'number' ...

关键心智模型:Python 的注解 ≈ "写在代码里的注释 + 给工具看的提示"。真正帮你抓错的不是 Python 本身,而是外部工具(mypy / Pyright)和编辑器(VS Code 的 Pylance)。注解的价值在开发期(补全、跳转、静态检查),不在运行期。 类比:就像你只写了 .ts 但从不跑 tsc——类型写了,但没人帮你校验。Python 想要"校验",得额外装并主动跑 mypy。

这正是"渐进类型(gradual typing)"的含义:你可以只给关键函数加注解,其余不写也能跑,新老代码混着来。和 TS 的 // @ts-nocheck 文件能和严格文件共存是一个味道。


三、容器、可选、联合:写法对照

Python 3.9+ 起,容器类型可以直接用内置的小写 list/dict/tuple/set 来写泛型参数(老教程里的 from typing import List 已不必要):

可选 / 联合,对应 TS 的 | null|

// TS 对比
function findUser(uid: number): string | null { ... }
function log(msg: string, tag: string | null = null): void { ... }

小坑:-> None 对应 TS 的 : void,表示"没有有意义的返回值"。别写成 -> null,Python 里空值的类型名是 None(首字母大写)。

如果你的运行环境还停留在 Python 3.8,list[int] / str | None 这种写法会报错,需要改用 from typing import List, Optional 的老写法。新项目用 3.10+ 即可无脑用新语法。


四、给函数和变量加注解的常见形态

// TS 对比:Callable 对应函数类型字面量
let onDone: (a: number, b: number) => number = add
let result: Record<string, number>

Callable[[int, int], int] 读法:第一个 [int, int] 是参数类型列表,第二个 int 是返回类型。这就是 TS 里 (a: number, b: number) => number 的 Python 写法。


五、结构化类型:从 interface 到 dataclass / TypedDict

TS 描述"对象长什么样"用 interface / type。Python 里有几种对应物,按场景选:

5.1 dataclass —— 最像 class 版的 interface

@dataclass 是标准库 dataclasses 提供的装饰器,自动根据注解生成 __init__ 等样板代码(装饰器机制见第 12 篇,这里先当"标签"用):

// TS 对比:interface 只描述形状,dataclass 还顺手给了构造函数
interface User {
  name: string
  age: number
  isVip?: boolean
}

类比边界:TS 的 interface 纯类型、运行期不存在;Python 的 dataclass真实的 class,运行期存在、能 new(直接 User(...))、能存数据。它更接近"自动帮你写好 constructor 的普通类"。

5.2 TypedDict —— 给"字典"标形状

如果数据天然是 dict(比如解析 JSON 拿到的),用 TypedDict 给它标键值结构,这个才最贴近 TS 的 interface

选型口诀:要"带行为/带方法的对象" → dataclass;只是"一坨带固定键的 JSON 字典" → TypedDict;这两个对应了 TS 里"用 class 还是用 interface 描述纯数据"的选择。


六、运行期真要校验?那是 Pydantic 的活

注解默认不校验(第二节),但 Web 开发里你真的需要"传进来的 JSON 字段类型不对就报错"。这件事 Python 标准库不做,由第三方库 Pydantic 来做——它是 FastAPI 的地基(FastAPI 见后续篇,此处先建立印象):

这正好补上第二节的"边界":

  • 普通注解 / dataclass / TypedDict → 不做运行期校验(只给工具看)
  • Pydantic BaseModel做运行期校验 + 类型转换 类比:Pydantic ≈ 前端的 zod——把"类型声明"升级成"运行期真正会拦截脏数据的校验器"。FastAPI 接收请求体时就是靠它把 JSON 自动校验并转成对象。

七、让注解真正帮到你:跑起 mypy / Pylance

注解写了不等于有用,得让工具来读。两条路通常一起用:

  1. 编辑器实时提示:VS Code 装 Pylance(基于 Pyright),打开就有补全、跳转、类型红线,体验接近写 TS。这步零配置,最划算。
  2. 命令行静态检查:装 mypy,在 CI 或本地批量扫全项目。
pip install mypy          # 安装静态类型检查器
mypy your_module.py       # 扫描指定文件/目录,报出类型不匹配

类比:mypy your_module.py ≈ 前端跑 tsc --noEmit——只做类型检查、不产出文件。区别在于 TS 几乎默认就跑,而 Python 的检查是你主动选择加上的一道关。不跑它,注解就只是好看的注释。


八、总结

  • 先给锚点:和 TS 的类型标注几乎一一对应:唯一要适应的小差异:返回值用 ->(箭头),不是冒号。
  • 边界一(最重要):注解默认不检查,写错也照样跑:这是前端从 TS 过来必踩的第一个认知坑。
  • 容器、可选、联合:写法对照:别写成 -> null,Python 里空值的类型名是 None(首字母大写)。
  • 给函数和变量加注解的常见形态:Callable[[int, int], int] 读法:第一个 [int, int] 是参数类型列表,第二个 int 是返回类型。
  • 结构化类型:从 interface 到 dataclass / TypedDict:类比边界:TS 的 interface 纯类型、运行期不存在;
  • 运行期真要校验?那是 Pydantic 的活:FastAPI 接收请求体时就是靠它把 JSON 自动校验并转成对象。

学完自测

选择所有正确答案;提交后逐项核对判断依据。

1在“类型注解”中,需要同时满足“先给锚点:和 TS 的类型标注几乎一一对应”与“边界一(最重要):注解默认不检查,写错也照样跑”。给定正文约束“返回值用 ->(箭头),不是冒号。”,哪些判断保持了原有处理机制?多选
2“类型注解”出现偏差:“在“类型注解 / 容器、可选、联合:写法对照”中,即使不满足“如果你的运行环境还停留在 Python 3.8,list[int] / str | None 这种写法会报错,需要改用 from typing import List,”,结果与副作用仍会保持不变。”已成为实际行为。围绕“容器、可选、联合:写法对照”与“给函数和变量加注解的常见形态”,哪些判断能定位被改变的职责或边界?多选
3评审“类型注解”方案时,验收条件包含“TS 描述"对象长什么样"用 interface / type。”。关于“结构化类型:从 interface 到 dataclass / TypedDict”与“dataclass —— 最像 class 版的 interface”的哪些决策符合正文机制?多选