feat(python进阶): 完成第三至第八课教学内容

This commit is contained in:
zhiye.sun
2026-08-07 17:14:48 +08:00
parent f7c820b0f4
commit aff05283a2
21 changed files with 3504 additions and 13 deletions

View File

@@ -0,0 +1,355 @@
# 第 2-4 课:推导式与简化写法
## 一、本课目标
完成本课后,你将能够:
1. 解释推导式是什么,以及它适合解决什么问题;
2. 使用列表推导式转换和筛选数据;
3. 使用字典推导式创建键值对应关系;
4. 使用集合推导式收集不重复的数据;
5. 使用简单的条件表达式返回二选一结果;
6. 在普通循环和推导式之间选择更清晰的写法;
7. 避免编写难以阅读的复杂推导式。
## 二、前置知识
学习本课前,需要掌握:
- 列表、字典和集合;
- `for` 循环和 `if` 条件判断;
- 函数、参数和返回值;
- 字典的 `get()` 方法。
## 三、推导式是什么
推导式Comprehension是 Python 根据可遍历数据快速创建新集合的一种写法。
第一阶段中,我们使用普通循环生成新列表:
```python
agent_names = []
for agent in agents:
agent_names.append(agent["name"])
```
使用列表推导式可以写成:
```python
agent_names = [agent["name"] for agent in agents]
```
两段代码的结果相同。推导式没有改变原来的 `agents`,而是创建并返回一个新列表。
## 四、列表推导式的基本结构
```python
[需要保存的值 for 临时变量 in 可遍历数据]
```
示例:
```python
agent_names = [agent["name"] for agent in agents]
```
建议从右向左理解:
1. `for agent in agents`:依次取出每个 Agent
2. `agent["name"]`:取得当前 Agent 的名称;
3. `[...]`:把每次得到的名称放入新列表。
推导式中的 `agent` 只是本次遍历使用的临时变量。
## 五、使用列表推导式筛选数据
普通循环写法:
```python
enabled_names = []
for agent in agents:
if agent["enabled"]:
enabled_names.append(agent["name"])
```
列表推导式写法:
```python
enabled_names = [
agent["name"]
for agent in agents
if agent["enabled"]
]
```
带筛选条件的结构是:
```python
[需要保存的值 for 临时变量 in 可遍历数据 if 条件]
```
只有条件为 `True` 的数据才会进入新列表。
## 六、转换与筛选的区别
转换表示每条数据都参与,但保存为另一种形式:
```python
name_lengths = [len(agent["name"]) for agent in agents]
```
筛选表示只有符合条件的数据才保留:
```python
enabled_agents = [agent for agent in agents if agent["enabled"]]
```
一个推导式也可以同时转换和筛选:
```python
enabled_names = [
agent["name"]
for agent in agents
if agent["enabled"]
]
```
## 七、字典推导式
字典推导式Dictionary Comprehension用于创建新字典
```python
tool_counts = {
agent["name"]: len(agent.get("tools", []))
for agent in agents
}
```
基本结构:
```python
{: for 临时变量 in 可遍历数据}
```
上述示例的结果类似:
```python
{
"代码助手": 2,
"聊天助手": 1,
}
```
字典的键不能重复。如果多个数据生成同一个键,后生成的值会覆盖之前的值。因此,应先确认选作键的数据具有合适的唯一性。
## 八、集合推导式
集合推导式Set Comprehension用于创建不包含重复值的集合
```python
models = {agent["model"] for agent in agents}
```
基本结构:
```python
{需要保存的值 for 临时变量 in 可遍历数据}
```
集合与字典都使用花括号,但字典推导式包含 `键: 值`,集合推导式只有一个值。
如果每个 Agent 中还有一个工具列表,可以使用两层 `for`
```python
unique_tools = {
tool
for agent in agents
for tool in agent.get("tools", [])
}
```
它等价于:
```python
unique_tools = set()
for agent in agents:
for tool in agent.get("tools", []):
unique_tools.add(tool)
```
本课只练习这一种容易理解的两层结构,不加入更多条件。
## 九、条件表达式
条件表达式Conditional Expression可以根据一个条件从两个值中选择一个
```python
status_text = "启用" if enabled else "停用"
```
执行顺序:
1. 检查 `enabled`
2. 条件为 `True`,得到 `"启用"`
3. 条件为 `False`,得到 `"停用"`
它等价于:
```python
if enabled:
status_text = "启用"
else:
status_text = "停用"
```
条件表达式适合简单的二选一。如果每个分支需要执行多步操作,应继续使用普通 `if...else`
## 十、筛选条件与条件表达式不要混淆
筛选只决定某条数据是否保留:
```python
[agent["name"] for agent in agents if agent["enabled"]]
```
条件表达式会为每条数据选择一个结果:
```python
[
"启用" if agent["enabled"] else "停用"
for agent in agents
]
```
第一段结果数量可能减少,第二段会为每个 Agent 产生一个状态文字。
## 十一、什么时候使用普通循环
以下情况优先使用普通循环:
- 每次遍历需要执行多个步骤;
- 需要输出中间过程;
- 包含多层条件判断;
- 推导式换行后仍难以理解;
- 需要在循环中提前 `break``return`
代码行数更少不代表代码更好。能够让自己和其他人快速理解的写法才更合适。
## 十二、完整示例
示例文件:
```text
02_python进阶/2_4_推导式与简化写法/comprehension_example.py
```
示例会演示:
1. 普通循环与列表推导式得到相同结果;
2. 筛选已启用的 Agent 名称;
3. 创建名称到工具数量的字典;
4. 收集所有不重复工具;
5. 使用条件表达式显示 Agent 状态。
## 十三、运行方法
在项目根目录运行示例:
```powershell
python .\02_python进阶\2_4_推导式与简化写法\comprehension_example.py
```
完成练习后运行:
```powershell
python .\02_python进阶\2_4_推导式与简化写法\practice.py
```
## 十四、预期结果
```text
普通循环结果:['代码助手', '测试助手']
列表推导式结果:['代码助手', '测试助手']
工具数量字典:{'代码助手': 2, '聊天助手': 1, '测试助手': 2}
不重复工具:['搜索', '测试', '终端']
Agent 状态:
- 代码助手:启用
- 聊天助手:停用
- 测试助手:启用
```
集合没有固定顺序。示例使用 `sorted()` 只是为了让输出顺序稳定,便于核对;集合本身仍然不保证顺序。
## 十五、常见错误
### 15.1 忘记最外层括号
列表推导式使用方括号,字典和集合推导式使用花括号。
### 15.2 把筛选条件写到错误位置
筛选条件写在 `for...in...` 后面:
```python
[agent["name"] for agent in agents if agent["enabled"]]
```
### 15.3 混淆字典和集合推导式
```python
{agent["name"] for agent in agents}
```
这是集合,因为没有 `键: 值`。创建字典必须写冒号。
### 15.4 推导式过于复杂
不要在一个推导式中堆叠多层条件表达式。看不懂时应立即改回普通循环。
### 15.5 误以为推导式会修改原数据
推导式通常创建新列表、新字典或新集合。除非其中主动修改对象,否则原集合不会因为推导式本身被替换。
## 十六、课堂练习
打开 `practice.py`,依次完成:
1. 创建全部 Agent 名称列表;
2. 筛选已启用 Agent 的名称;
3. 创建名称到模型的字典;
4. 收集不重复工具;
5. 使用条件表达式返回状态文字;
6.`main()` 中保存并输出所有返回结果。
题目已经给出参数、返回值、示例数据和预期结果。请先独立完成。
## 十七、参考答案
参考答案暂不写入练习文件。你完成后,我会从正确性、可读性和知识掌握情况三个方面验证,并重点检查你是否能够解释推导式,而不只是记住格式。
## 十八、本课小结
- 推导式可以根据已有数据创建新集合;
- 列表推导式使用 `[]`
- 字典推导式使用 `{键: 值}`
- 集合推导式使用 `{值}`
- 末尾的 `if` 用于筛选;
- 条件表达式用于从两个结果中选择一个;
- 推导式适合简单逻辑,复杂逻辑应使用普通循环;
- 清晰比简短更重要。
## 十九、验收标准
- 能用普通循环和列表推导式实现相同功能;
- 能解释列表推导式各部分的执行顺序;
- 能使用末尾 `if` 筛选数据;
- 能创建名称到模型的字典;
- 能使用集合推导式去重;
- 能正确使用简单条件表达式;
- 能区分筛选条件与条件表达式;
- 所有练习函数通过 `return` 返回结果;
- 推导式不会修改原始 Agent 列表;
- 没有为了简短而编写难以阅读的复杂表达式。

View File

@@ -0,0 +1,77 @@
# 第 2-4 课完整示例:推导式与简化写法
#
# 本示例先展示普通循环,再展示作用相同的推导式。
# 推导式适合简单的数据转换和筛选,不应为了缩短代码而堆叠复杂逻辑。
def get_enabled_agent_names(agents):
"""使用列表推导式返回所有已启用 Agent 的名称。"""
return [agent["name"] for agent in agents if agent["enabled"]]
def get_agent_tool_counts(agents):
"""使用字典推导式建立 Agent 名称与工具数量的对应关系。"""
return {
agent["name"]: len(agent.get("tools", []))
for agent in agents
}
def get_unique_tools(agents):
"""使用集合推导式收集不重复的工具名称。"""
return {
tool
for agent in agents
for tool in agent.get("tools", [])
}
def get_status_text(agent):
"""使用条件表达式返回简短的中文状态。"""
return "启用" if agent["enabled"] else "停用"
def main():
"""准备 Agent 数据,并演示四种简化写法。"""
agents = [
{
"name": "代码助手",
"enabled": True,
"tools": ["搜索", "终端"],
},
{
"name": "聊天助手",
"enabled": False,
"tools": ["搜索"],
},
{
"name": "测试助手",
"enabled": True,
"tools": ["终端", "测试"],
},
]
# 普通循环写法:先创建空列表,再逐条判断和添加。
names_from_loop = []
for agent in agents:
if agent["enabled"]:
names_from_loop.append(agent["name"])
# 列表推导式可以简洁表达同一个“遍历、筛选、保存”过程。
enabled_names = get_enabled_agent_names(agents)
tool_counts = get_agent_tool_counts(agents)
unique_tools = get_unique_tools(agents)
print(f"普通循环结果:{names_from_loop}")
print(f"列表推导式结果:{enabled_names}")
print(f"工具数量字典:{tool_counts}")
print(f"不重复工具:{sorted(unique_tools)}")
print("Agent 状态:")
for agent in agents:
status_text = get_status_text(agent)
print(f"- {agent['name']}{status_text}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,65 @@
# 第 2-4 课课堂练习:推导式与简化写法
#
# 请先阅读讲义并运行完整示例,再按照题目顺序完成。
# 每个函数都需要使用 return 返回结果,不要只在函数中 print()。
# 不要删除题目、测试数据、预期结果和自查注释。
# 第一部分:定义 get_agent_names(agents) 函数
# 1. 使用列表推导式遍历 agents。
# 2. 取出每个字典中的 name。
# 3. return 生成的新列表,不要修改原列表。
# 4. 调用后保存结果并输出。
# 5. 预期结果:['代码助手', '聊天助手', '测试助手']。
# 第二部分:定义 get_enabled_agent_names(agents) 函数
# 1. 使用带 if 的列表推导式。
# 2. 只保留 enabled 为 True 的 Agent 名称。
# 3. return 新列表。
# 4. 预期结果:['代码助手', '测试助手']。
# 第三部分:定义 get_model_mapping(agents) 函数
# 1. 使用字典推导式。
# 2. 字典的键是 Agent 名称,值是模型名称。
# 3. return 新字典。
# 4. 预期结果:
# {'代码助手': 'gpt-5', '聊天助手': 'o3', '测试助手': 'gpt-5'}。
# 第四部分:定义 get_unique_tools(agents) 函数
# 1. 使用集合推导式和两层 for。
# 2. 外层遍历 agents内层遍历每个 agent.get("tools", [])。
# 3. return 不重复的工具集合。
# 4. 预期集合包含:搜索、终端、测试。
# 5. 集合没有固定顺序,验收时比较内容,不比较显示顺序。
# 第五部分:定义 get_status_text(enabled) 函数
# 1. 使用条件表达式,不使用多行 if...else。
# 2. enabled 为 True 时 return "启用",否则 return "停用"。
# 3. 分别传入 True 和 False。
# 4. 预期结果分别是“启用”和“停用”。
# 第六部分:定义 main() 函数
# 1. 依次调用前面五个函数,并把每个返回值保存到有意义的变量中。
# 2. 使用 print() 输出所有 Agent 名称、启用名称、模型字典和工具集合。
# 3. 调用 get_status_text(agents[0]["enabled"]) 并输出第一个 Agent 的状态。
# 4. 添加程序入口判断,直接运行本文件时调用 main()。
# 最终验收测试:
# 1. get_agent_names(agents) 返回三个名称;
# 2. get_enabled_agent_names(agents) 只返回两个启用的名称;
# 3. get_model_mapping(agents) 返回名称到模型的正确映射;
# 4. get_unique_tools(agents) 返回三个不重复工具;
# 5. get_status_text(True) 返回“启用”;
# 6. get_status_text(False) 返回“停用”;
# 7. 所有函数都使用 return 返回结果;
# 8. 推导式没有修改原始 agents
# 9. 没有使用嵌套条件表达式或难以阅读的复杂推导式。
# 完成后自查:
# 1. 是否能从左到右解释每个推导式的执行过程;
# 2. 是否分清列表、字典和集合推导式使用的括号;
# 3. 是否知道筛选条件写在推导式末尾;
# 4. 是否知道条件表达式的结果分别写在 if 两侧;
# 5. 是否优先保证可读性,而不是一味缩短代码;
# 6. 是否保留了完整题目和验收说明。