feat(python进阶): 完成第三至第八课教学内容
This commit is contained in:
383
02_python进阶/2_7_类型注解/README.md
Normal file
383
02_python进阶/2_7_类型注解/README.md
Normal file
@@ -0,0 +1,383 @@
|
||||
# 第 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` 标注输出函数;
|
||||
- 能说明注解与类型转换的区别;
|
||||
- 能说明类型注解通常不会自动强制检查运行时数据;
|
||||
- 所有注解与函数真实返回值保持一致。
|
||||
84
02_python进阶/2_7_类型注解/practice.py
Normal file
84
02_python进阶/2_7_类型注解/practice.py
Normal file
@@ -0,0 +1,84 @@
|
||||
# 第 2-7 课课堂练习:类型注解
|
||||
#
|
||||
# 请先阅读讲义并运行完整示例,再按照题目顺序完成。
|
||||
# 每个函数都需要写参数类型和返回值类型。
|
||||
# 不要删除题目、测试数据、预期结果和自查注释。
|
||||
|
||||
|
||||
# 第一部分:定义 format_task_status(title, completed) 函数
|
||||
# 1. 参数 title 标注为 str。
|
||||
# 2. 参数 completed 标注为 bool。
|
||||
# 3. 返回值标注为 str。
|
||||
# 4. completed 为 True 时状态文字是“已完成”,否则是“未完成”。
|
||||
# 5. return f"{title}|状态:{status_text}"。
|
||||
# 6. 传入“学习类型注解”和 True,预期返回:
|
||||
# “学习类型注解|状态:已完成”。
|
||||
|
||||
# print(format_task_status("学习类型注解", True))
|
||||
|
||||
# 第二部分:定义 build_task(title, status) 函数
|
||||
# 1. 两个参数都标注为 str。
|
||||
# 2. 返回值标注为 dict[str, str]。
|
||||
# 3. return 包含 title 和 status 的新字典。
|
||||
# 4. 传入“复习生成器”和“未开始”,预期返回:
|
||||
# {"title": "复习生成器", "status": "未开始"}。
|
||||
|
||||
# print(build_task("复习生成器", "未开始"))
|
||||
|
||||
# 第三部分:定义 get_task_titles(tasks) 函数
|
||||
# 1. 参数标注为 list[dict[str, str]]。
|
||||
# 2. 返回值标注为 list[str]。
|
||||
# 3. 使用列表推导式取得每个任务的 title。
|
||||
# 4. return 新列表,不要修改原 tasks。
|
||||
# 5. 使用顶部测试数据,预期返回:
|
||||
# ["学习类型注解", "完成课堂练习"]。
|
||||
|
||||
# print(get_task_titles(tasks))
|
||||
|
||||
# 第四部分:定义 calculate_completion_rate(completed_count, total_count) 函数
|
||||
# 1. 两个参数都标注为 int。
|
||||
# 2. 返回值标注为 float | None,表示可能返回浮点数,也可能返回 None。
|
||||
# 3. total_count 为 0 时 return None。
|
||||
# 4. 否则 return completed_count / total_count。
|
||||
# 5. 传入 3 和 4,预期返回 0.75。
|
||||
# 6. 传入 0 和 0,预期返回 None,程序不能崩溃。
|
||||
|
||||
# print(calculate_completion_rate(3,4))
|
||||
# print(calculate_completion_rate(0,0))
|
||||
|
||||
# 第五部分:定义 print_task(task) 函数
|
||||
# 1. 参数标注为 dict[str, str]。
|
||||
# 2. 返回值标注为 None,因为本函数只输出,不返回业务结果。
|
||||
# 3. 按“任务:标题|状态:状态文字”的格式输出。
|
||||
# 4. 传入第一个测试任务,预期输出:
|
||||
# “任务:学习类型注解|状态:已完成”。
|
||||
|
||||
# print_task(tasks[0])
|
||||
|
||||
# 第六部分:定义 main() 函数
|
||||
# 1. 返回值标注为 None。
|
||||
# 2. 依次调用前面五个函数,并保存需要使用的返回值。
|
||||
# 3. 输出格式化状态、新任务字典、任务标题列表和两种完成率。
|
||||
# 4. 调用 print_task(tasks[0]) 输出第一个任务。
|
||||
# 5. 添加程序入口判断,直接运行本文件时调用 main()。
|
||||
|
||||
# 最终验收测试:
|
||||
# 1. format_task_status() 的参数和返回值注解正确;
|
||||
# 2. build_task() 返回正确字典,并标注 dict[str, str];
|
||||
# 3. get_task_titles() 返回正确列表,并标注 list[str];
|
||||
# 4. calculate_completion_rate(3, 4) 返回 0.75;
|
||||
# 5. calculate_completion_rate(0, 0) 返回 None;
|
||||
# 6. 完成率返回类型标注为 float | None;
|
||||
# 7. print_task() 和 main() 的返回值标注为 None;
|
||||
# 8. 原始 tasks 没有被修改;
|
||||
# 9. 直接运行程序时,所有结果均正确输出。
|
||||
|
||||
|
||||
# 完成后自查:
|
||||
# 1. 是否知道冒号后写参数或变量类型;
|
||||
# 2. 是否知道 -> 后写函数返回值类型;
|
||||
# 3. 是否能区分 list[str] 和 dict[str, str];
|
||||
# 4. 是否理解 float | None 表示两种可能结果;
|
||||
# 5. 是否知道只输出的函数返回值标注为 None;
|
||||
# 6. 是否理解类型注解通常不会自动检查运行时数据;
|
||||
# 7. 是否保留了完整题目和验收说明。
|
||||
53
02_python进阶/2_7_类型注解/type_annotation_example.py
Normal file
53
02_python进阶/2_7_类型注解/type_annotation_example.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# 第 2-7 课完整示例:类型注解
|
||||
#
|
||||
# 类型注解用于说明变量、参数和返回值预期使用的类型。
|
||||
# Python 通常不会在运行时自动强制检查这些注解。
|
||||
|
||||
|
||||
def format_agent_status(name: str, enabled: bool) -> str:
|
||||
"""根据名称和启用状态返回中文说明。"""
|
||||
status_text = "启用" if enabled else "停用"
|
||||
return f"{name}|状态:{status_text}"
|
||||
|
||||
|
||||
def count_tools(tools: list[str]) -> int:
|
||||
"""返回工具名称列表中的元素数量。"""
|
||||
return len(tools)
|
||||
|
||||
|
||||
def find_agent_model(
|
||||
model_mapping: dict[str, str],
|
||||
agent_name: str,
|
||||
) -> str | None:
|
||||
"""返回指定 Agent 的模型,找不到时返回 None。"""
|
||||
return model_mapping.get(agent_name)
|
||||
|
||||
|
||||
def print_agent_report(name: str, tools: list[str]) -> None:
|
||||
"""输出 Agent 报告,本函数不返回业务结果。"""
|
||||
print(f"Agent 名称:{name}")
|
||||
print(f"工具数量:{count_tools(tools)}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""准备带类型注解的数据并调用示例函数。"""
|
||||
agent_name: str = "代码助手"
|
||||
enabled: bool = True
|
||||
tools: list[str] = ["搜索", "终端"]
|
||||
model_mapping: dict[str, str] = {
|
||||
"代码助手": "gpt-5",
|
||||
"聊天助手": "o3",
|
||||
}
|
||||
|
||||
status_text = format_agent_status(agent_name, enabled)
|
||||
model_name = find_agent_model(model_mapping, agent_name)
|
||||
missing_model = find_agent_model(model_mapping, "测试助手")
|
||||
|
||||
print(status_text)
|
||||
print(f"模型:{model_name}")
|
||||
print(f"不存在的模型:{missing_model}")
|
||||
print_agent_report(agent_name, tools)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user