第 2-7 课:类型注解
一、本课目标
完成本课后,你将能够:
- 解释类型注解是什么,以及它能解决什么问题;
- 为变量、函数参数和返回值添加类型注解;
- 标注
str、int、float和bool; - 标注字符串列表与字符串字典;
- 使用
类型 | None表示结果可能不存在; - 使用
-> None标注只执行操作、不返回业务结果的函数; - 理解类型注解通常不会在运行时自动强制检查数据。
二、前置知识
学习本课前,需要掌握:
- 变量和基本数据类型;
- 列表与字典;
- 函数、参数与返回值;
- 条件判断;
None的基本含义。
三、为什么需要类型注解
观察下面的函数:
def format_status(name, enabled):
...
只看函数定义,我们无法立即确定:
name应该传字符串还是字典;enabled应该传布尔值还是文字;- 函数最后返回什么类型。
添加类型注解(Type Hint):
def format_status(name: str, enabled: bool) -> str:
...
现在可以看出:
name预期是字符串;enabled预期是布尔值;-> str表示预期返回字符串。
类型注解的主要作用是帮助人和工具理解代码。
四、类型注解不会自动改变数据
age: int = 18
int 说明变量预期保存整数,但它不会执行类型转换。下面两段代码不同:
age: int = "18" # 只是写了注解,值仍然是字符串
age = int("18") # 真正把字符串转换为整数
Python 通常也不会因为注解与实际值不一致而立即阻止程序运行。编辑器或静态类型检查工具可以提前提示问题。
静态类型检查(Static Type Checking)表示在不真正执行全部业务代码的情况下,根据注解检查可能的类型错误。
五、变量类型注解
基本格式:
变量名: 类型 = 值
常见示例:
agent_name: str = "代码助手"
tool_count: int = 2
completion_rate: float = 0.75
enabled: bool = True
冒号后面是预期类型,等号后面仍然是实际值。
简单变量的类型通常很容易从值中看出,因此不必强迫每个局部变量都写注解。类型不明显或需要强调时再添加。
六、参数和返回值注解
def get_status_text(enabled: bool) -> str:
return "启用" if enabled else "停用"
enabled: bool是参数注解;-> str是返回值注解;- 注解不影响函数正常调用方式。
调用仍然写成:
status_text = get_status_text(True)
七、常见基本类型
本课使用以下类型:
| 注解 | 表示的数据 |
|---|---|
str |
字符串 |
int |
整数 |
float |
浮点数 |
bool |
布尔值 |
None |
没有业务结果 |
类型名称不需要加引号:
name: str = "代码助手"
不要写成:
name: "str" = "代码助手"
字符串形式的类型注解有其他用途,本课暂不展开。
八、列表类型注解
字符串列表:
tools: list[str] = ["搜索", "终端"]
可以从外向内理解:
list:这是列表;[str]:列表中的元素预期是字符串。
函数示例:
def count_tools(tools: list[str]) -> int:
return len(tools)
返回字符串列表:
def get_names() -> list[str]:
return ["代码助手", "聊天助手"]
九、字典类型注解
名称到模型的对应关系,可以标注为:
models: dict[str, str] = {
"代码助手": "gpt-5",
"聊天助手": "o3",
}
dict[str, str] 表示:
- 第一个
str:字典键是字符串; - 第二个
str:字典值也是字符串。
任务列表可能写成:
tasks: list[dict[str, str]] = [
{"title": "学习类型注解", "status": "已完成"},
]
从外向内理解:这是一个列表,列表中每项是字典,字典的键和值都是字符串。
十、结果可能是 None
查找操作可能找到字符串,也可能找不到:
def find_model(
models: dict[str, str],
agent_name: str,
) -> str | None:
return models.get(agent_name)
str | None 表示返回值有两种可能:
- 找到时返回
str; - 未找到时返回
None。
符号 | 可以理解为“或者”。
调用者应检查:
model_name = find_model(models, "测试助手")
if model_name is None:
print("没有找到模型。")
十一、返回值标注为 None
只负责输出、不返回业务结果的函数:
def print_agent(name: str) -> None:
print(name)
-> None 表示调用者不应期待它返回可供后续使用的业务数据。
程序入口也常写成:
def main() -> None:
...
十二、类型注解和默认值
默认值写在类型注解之后:
def create_agent(name: str, enabled: bool = True) -> str:
...
阅读顺序:
- 参数名是
enabled; - 类型是
bool; - 默认值是
True。
不要把类型和默认值的位置写反。
十三、类型注解能带来什么
类型注解可以帮助:
- 阅读者更快理解参数和返回值;
- 编辑器提供更准确的自动补全;
- 编辑器提前提示可能的类型错误;
- 重构代码时发现受影响的位置;
- 静态类型检查工具检查大型项目。
类型注解不能代替:
- 运行时数据校验;
try...except异常处理;- 业务规则判断;
- 单元测试。
例如,Web 接口收到的外部数据仍然需要真实校验,不能只依靠注解。
十四、完整示例
示例文件:
02_python进阶/2_7_类型注解/type_annotation_example.py
示例会演示:
- 字符串和布尔参数注解;
- 字符串列表和字符串字典;
- 整数返回值;
str | None;- 只输出函数的
-> None。
十五、运行方法
在项目根目录运行示例:
python .\02_python进阶\2_7_类型注解\type_annotation_example.py
完成练习后运行:
python .\02_python进阶\2_7_类型注解\practice.py
十六、预期结果
代码助手|状态:启用
模型:gpt-5
不存在的模型:None
Agent 名称:代码助手
工具数量:2
十七、常见错误
17.1 把注解当作类型转换
count: int = "2"
实际值仍然是字符串。需要转换时必须使用 int("2")。
17.2 忘记返回值箭头
参数使用冒号,返回值使用 ->:
def count_tools(tools: list[str]) -> int:
...
17.3 列表没有写元素类型
只写 list 无法说明元素是什么。本课优先写成 list[str]。
17.4 混淆字典键和值的类型
dict[str, int] 表示字符串键和整数值,前后顺序不能颠倒。
17.5 可能返回 None 却只标注字符串
如果函数可能找不到结果,应标注 str | None,提醒调用者处理空结果。
17.6 认为有注解就不需要测试
类型正确不代表业务结果正确。边界条件、异常和实际输出仍需验证。
十八、课堂练习
打开 practice.py,依次完成:
- 为任务状态格式化函数添加基本类型;
- 创建并返回字符串字典;
- 处理任务字典列表;
- 使用
float | None表示两种完成率结果; - 使用
-> None标注输出函数和主函数; - 运行正常与零任务数用例。
十九、参考答案
参考答案暂不写入练习文件。完成后,我会验证函数行为与 __annotations__ 中的实际注解,并检查注解是否与真实返回值一致。
二十、本课小结
- 类型注解说明代码预期使用的类型;
- 参数类型写在冒号后,返回类型写在
->后; list[str]表示字符串列表;dict[str, str]表示字符串键和字符串值;str | None表示可能返回字符串或None;- 只执行操作的函数可以标注
-> None; - 类型注解不会自动转换或验证运行时数据;
- 类型注解提高可读性,但不能代替校验、异常处理和测试。
二十一、验收标准
- 能解释类型注解的作用;
- 能为基本类型参数和返回值添加注解;
- 能正确标注字符串列表;
- 能正确标注字符串字典;
- 能看懂嵌套的
list[dict[str, str]]; - 能使用
float | None表达两种结果; - 能使用
-> None标注输出函数; - 能说明注解与类型转换的区别;
- 能说明类型注解通常不会自动强制检查运行时数据;
- 所有注解与函数真实返回值保持一致。