Files

384 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第 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` 标注输出函数;
- 能说明注解与类型转换的区别;
- 能说明类型注解通常不会自动强制检查运行时数据;
- 所有注解与函数真实返回值保持一致。