Files
PythonLearn/02_python进阶/2_4_推导式与简化写法/README.md

356 lines
8.5 KiB
Markdown
Raw 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-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 列表;
- 没有为了简短而编写难以阅读的复杂表达式。