# 第 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 列表; - 没有为了简短而编写难以阅读的复杂表达式。