# 第 1-13 课:函数进阶 ## 一、本课目标 完成本课后,你将能够: 1. 使用 `return` 一次返回多个结果; 2. 使用提前返回减少不必要的嵌套; 3. 使用 `*args` 接收任意数量的位置参数; 4. 使用 `**kwargs` 接收任意数量的关键字参数; 5. 使用 `*` 和 `**` 在调用函数时解包数据; 6. 根据不同任务选择普通参数、`*args` 或 `**kwargs`; 7. 继续练习让函数保持清晰、职责单一。 ## 二、前置知识 学习本课前,需要理解: - 如何使用 `def` 定义函数; - 如何调用函数; - 位置参数、关键字参数和默认参数; - `print()` 与 `return` 的区别; - 列表、元组和字典; - 条件判断和循环。 ## 三、一次返回多个结果 一个函数可以在 `return` 后面写多个值: ```python def get_agent_summary(agent): return agent["name"], len(agent.get("tools", [])) agent_name, tool_count = get_agent_summary({ "name": "代码助手", "tools": ["搜索", "终端"], }) print(agent_name) print(tool_count) ``` Python 实际上会先把多个返回值组合成一个元组,再把元组解包给多个变量。 也可以只用一个变量接收: ```python summary = get_agent_summary({"name": "聊天助手", "tools": []}) print(summary) print(type(summary)) ``` 此时 `summary` 是元组,例如 `("聊天助手", 0)`。 ## 四、提前返回 `return` 不只负责返回结果,还会立即结束当前函数。 ```python def get_agent_status(agent): if not agent.get("enabled", False): return "停用" return "启用" ``` 如果 Agent 没有启用,函数执行第一个 `return` 后就结束,不会继续执行下面的代码。 这种写法称为提前返回(Early Return)。它适合先处理无效数据或特殊情况,避免出现太多层缩进。 ## 五、使用 `*args` 接收多个位置参数 有时无法提前确定调用者会传入几个位置参数,这时可以使用 `*args`: ```python def count_tools(*tools): return len(tools) print(count_tools("搜索")) print(count_tools("搜索", "终端", "计算器")) ``` 这里的 `args` 是 arguments 的缩写,表示“多个参数”。 需要注意: - 参数名前面的 `*` 才是关键; - `args` 是约定俗成的名称,也可以换成其他名称; - 函数内部的 `tools` 是一个元组; - 即使没有传入任何工具,`tools` 也是空元组。 ```python def show_tools(*tools): print(tools) print(type(tools)) ``` ## 六、普通参数和 `*args` 一起使用 普通参数可以写在 `*args` 前面: ```python def build_tool_message(agent_name, *tools): return f"{agent_name} 拥有 {len(tools)} 个工具" message = build_tool_message("代码助手", "搜索", "终端") print(message) ``` 调用时,第一个位置参数交给 `agent_name`,剩余位置参数都被收集到 `tools` 元组中。 ## 七、使用 `**kwargs` 接收多个关键字参数 如果无法提前确定调用者会传入哪些关键字参数,可以使用 `**kwargs`: ```python def build_agent_config(**config): return config agent_config = build_agent_config( name="代码助手", model="gpt-5", enabled=True, ) print(agent_config) ``` `kwargs` 是 keyword arguments 的缩写,中文可理解为“多个关键字参数”。 函数内部的 `config` 是字典: ```python { "name": "代码助手", "model": "gpt-5", "enabled": True, } ``` 同样,参数名前面的两个星号 `**` 才是关键,`kwargs` 只是常见命名。 ## 八、普通参数和 `**kwargs` 一起使用 ```python def create_agent(name, **settings): return { "name": name, "settings": settings, } agent = create_agent("代码助手", model="gpt-5", enabled=True) print(agent) ``` `name` 接收普通参数,其他关键字参数被收集到 `settings` 字典中。 ## 九、调用函数时使用 `*` 解包列表或元组 星号也可以出现在函数调用中。此时它表示把列表或元组中的元素依次作为位置参数传入。 ```python def add_three_numbers(first, second, third): return first + second + third numbers = [10, 20, 30] total = add_three_numbers(*numbers) print(total) ``` 上面的调用相当于: ```python total = add_three_numbers(10, 20, 30) ``` 元素数量必须与函数所需的位置参数数量匹配,否则会产生 `TypeError`。 ## 十、调用函数时使用 `**` 解包字典 两个星号可以把字典解包成关键字参数: ```python def describe_agent(name, model): return f"{name} 使用 {model}" agent_data = { "name": "代码助手", "model": "gpt-5", } description = describe_agent(**agent_data) print(description) ``` 上面的调用相当于: ```python description = describe_agent(name="代码助手", model="gpt-5") ``` 字典的键必须与函数参数名一致。 ## 十一、如何选择参数形式 ### 使用普通参数 参数数量固定、含义明确时,优先使用普通参数: ```python def get_total(first_number, second_number): return first_number + second_number ``` ### 使用 `*args` 需要接收数量不固定的同类位置参数时使用: ```python def count_tools(*tools): return len(tools) ``` ### 使用 `**kwargs` 需要接收数量或名称不固定的配置项时使用: ```python def build_config(**config): return config ``` 不要为了显得代码高级而强行使用 `*args` 或 `**kwargs`。参数固定时,普通参数通常更清楚。 ## 十二、完整示例 本课示例文件为: ```text 01_python基础/1_13_函数进阶/advanced_functions.py ``` 示例会依次演示: 1. 返回多个结果; 2. 提前返回; 3. 使用 `*args`; 4. 使用 `**kwargs`; 5. 解包列表和字典后调用函数。 ## 十三、运行方法 在项目根目录执行: ```powershell python .\01_python基础\1_13_函数进阶\advanced_functions.py ``` 运行练习: ```powershell python .\01_python基础\1_13_函数进阶\practice.py ``` ## 十四、示例运行结果 ```text 一、返回多个结果 Agent 名称:代码助手 工具数量:2 ============================== 二、提前返回 启用 停用 ============================== 三、任意数量的位置参数 工具数量:3 ============================== 四、任意数量的关键字参数 {'name': '代码助手', 'model': 'gpt-5', 'enabled': True} ============================== 五、调用时解包数据 数字总和:60 代码助手 使用 gpt-5 ``` 字典的显示格式可能因环境而略有差异,但键和值应当一致。 ## 十五、关键代码解析 ```python def count_tools(*tools): return len(tools) ``` 执行顺序如下: 1. 调用者传入零个或多个位置参数; 2. `*` 把这些参数收集到 `tools`; 3. `tools` 在函数内部是元组; 4. `len()` 统计元组中的元素数量; 5. `return` 把数量交给调用者。 ```python def build_agent_config(**config): return config ``` 执行顺序如下: 1. 调用者传入零个或多个关键字参数; 2. `**` 把参数名称和值收集到 `config`; 3. `config` 在函数内部是字典; 4. 函数把字典返回给调用者。 ## 十六、常见错误 ### 16.1 混淆 `*args` 和 `**kwargs` - `*args` 收集位置参数,函数内部得到元组; - `**kwargs` 收集关键字参数,函数内部得到字典。 ### 16.2 忘记星号 `args` 和 `kwargs` 本身只是普通名称。真正产生收集效果的是 `*` 和 `**`。 ### 16.3 解包后的参数数量不匹配 如果函数需要三个参数,列表却只有两个元素,会产生 `TypeError`,表示调用时提供的参数数量不正确。 ### 16.4 字典键与参数名不一致 使用 `**dictionary` 调用函数时,字典键必须能对应函数参数名。 ### 16.5 在 `return` 后继续编写必须执行的代码 函数执行到 `return` 就会结束,写在同一执行路径后面的代码不会运行。 ### 16.6 所有函数都使用可变参数 固定且明确的数据应继续使用普通参数。过度使用可变参数会降低可读性。 ## 十七、课堂练习 打开: ```text 01_python基础/1_13_函数进阶/practice.py ``` 练习题已经明确提供输入数据、实现步骤、返回要求和预期结果。请先独立完成,再进行验证。 ## 十八、本课小结 1. `return value1, value2` 可以返回多个结果; 2. 多个返回值实际会组合成元组; 3. 提前返回可以尽早结束函数; 4. `*args` 收集位置参数并形成元组; 5. `**kwargs` 收集关键字参数并形成字典; 6. 调用函数时,`*` 可以解包列表或元组; 7. 调用函数时,`**` 可以解包字典; 8. 参数固定时应优先使用普通参数。 ## 十九、验收标准 - 能正确接收函数的多个返回值; - 能解释 `return` 为什么会结束函数; - 能使用 `*args` 接收不同数量的位置参数; - 能说明 `*args` 在函数内部是元组; - 能使用 `**kwargs` 接收关键字参数; - 能说明 `**kwargs` 在函数内部是字典; - 能使用 `*` 解包列表或元组后调用函数; - 能使用 `**` 解包字典后调用函数; - 示例程序和练习程序可以正常运行。