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