目录7.1 函数概述与定义7.1.1 为什么需要函数7.1.2 函数的定义与调用7.1.3 函数文档字符串7.2 函数参数详解7.2.1 位置参数7.2.2 默认参数7.2.3 关键字参数7.2.4 可变参数*args 和 **kwargs7.3 函数返回值7.3.1 单个返回值7.3.2 多个返回值实际返回元组7.3.3 返回 None7.4 变量作用域7.5 匿名函数lambda7.6 模块Module7.6.1 什么是模块7.6.2 if __name__ __main__ 的作用7.6.3 Python 标准库常用模块7.7 类型注解Type Hints7.8 异常处理基础7.8.1 什么是异常7.8.2 常见内置异常7.8.3 try-except 基础语法7.9 异常处理高级用法7.9.1 捕获多种异常7.9.2 else 子句7.9.3 finally 子句7.9.4 主动抛出异常raise7.9.5 自定义异常7.10 综合实战健壮的 LLM 调用模块7.11 本章小结上一章我们学习了列表、字典等数据结构能高效地组织数据。但当你写了一段有用的代码比如调用大模型 API、计算 token 消耗你可能会想在多个地方重复使用它。函数就是用来封装一段可重复使用的代码块给它起个名字随时可以“调用”。本章将学习如何定义函数、传递参数、返回值以及如何把函数组织成模块就像调用openai.ChatCompletion.create()一样。所有例子都围绕大模型开发场景为后续调用真实 API 打下基础。最后还会深入讲解异常处理让程序更加健壮。7.1 函数概述与定义7.1.1 为什么需要函数理论讲解在编程中如果没有函数当你需要重复执行某段逻辑时如计算文本的token数量、校验模型参数你不得不复制粘贴同样的代码。这样做不仅导致代码冗余、难以维护而且一旦逻辑需要修改例如分词规则变化你必须修改所有复制的地方极易出错。函数的核心价值在于封装将一段具有特定功能的代码打包成一个独立单元。复用可以在程序的多个位置调用同一个函数避免重复编写。可维护性修改函数内部实现所有调用处自动生效。可读性通过有意义的函数名如count_tokens让代码逻辑一目了然。7.1.2 函数的定义与调用理论讲解Python中使用def关键字定义函数。基本语法为def 函数名(参数1, 参数2, ...): 可选的文档字符串 函数体代码块 return 返回值 # 可选函数名应遵循变量命名规则小写字母、下划线、有意义。参数列表可以为空也可以有多个参数。函数体必须缩进通常 4 个空格。return语句用于返回结果若没有return函数默认返回None。调用函数时使用函数名(参数值)的形式。案例代码# 定义函数模拟计算token数 def count_tokens(text): 模拟计算文本的token数量简单按空格分词 tokens text.split() return len(tokens) # 调用函数 msg Hello, how are you? num count_tokens(msg) print(f消息 {msg} 包含 {num} 个 token) # 输出消息 Hello, how are you? 包含 4 个 token7.1.3 函数文档字符串理论讲解紧跟在函数定义之后的第一个字符串三个双引号包裹称为文档字符串docstring。它用于说明函数的作用、参数含义、返回值等。可以通过help(函数名)或函数名.__doc__查看也是自动生成文档的基础。案例代码def count_tokens(text): 模拟计算文本的token数量简单按空格分词 return len(text.split()) print(count_tokens.__doc__) # 输出模拟计算文本的token数量简单按空格分词7.2 函数参数详解7.2.1 位置参数理论讲解位置参数是最常见的参数形式。调用函数时传入的参数按照定义时的顺序依次匹配给参数。参数的个数必须与定义一致否则会报错。案例代码def send_prompt(model, prompt, temperature): 发送提示词到指定模型 print(f使用 {model}, 温度 {temperature}, 发送: {prompt}) # 顺序必须一致 send_prompt(gpt-4, 你好, 0.7) # 输出使用 gpt-4, 温度 0.7, 发送: 你好7.2.2 默认参数理论讲解可以为参数指定默认值。调用时如果不传入该参数则使用默认值。默认参数使函数更加灵活。重要规则默认参数必须放在所有位置参数之后。案例代码def call_llm(prompt, modelgpt-3.5, temperature0.7): 调用大模型模拟 print(f模型: {model}, 温度: {temperature}) print(f提示词: {prompt}) return f这是 {model} 的回复 # 只传必需的 prompt response1 call_llm(讲个笑话) # 指定 model response2 call_llm(写首诗, modelgpt-4) # 指定所有参数 response3 call_llm(解释AI, claude-3, 1.2)7.2.3 关键字参数理论讲解调用函数时可以使用参数名值的形式指定参数称为关键字参数。关键字参数可以无视位置顺序但必须写在所有位置参数之后。案例代码# 使用关键字参数顺序可以打乱 call_llm(prompt你好, temperature0.9, modelgpt-4)7.2.4 可变参数*args和**kwargs理论讲解*args用于接收任意多个位置参数这些参数会被打包成一个元组。**kwargs用于接收任意多个关键字参数这些参数会被打包成一个字典。这在封装函数或编写装饰器时非常有用。案例代码def log_api_call(func_name, *args, **kwargs): 记录API调用日志 print(f调用函数: {func_name}) print(f位置参数: {args}) print(f关键字参数: {kwargs}) log_api_call(chat, gpt-4, temperature0.8, max_tokens100) # 输出 # 调用函数: chat # 位置参数: (gpt-4,) # 关键字参数: {temperature: 0.8, max_tokens: 100}7.3 函数返回值7.3.1 单个返回值理论讲解函数通过return语句返回一个值。返回后函数立即结束。案例代码def get_model_cost(model): 返回模型每1k token的费用美元 prices {gpt-4: 0.03, gpt-3.5: 0.002, claude-3: 0.025} return prices.get(model, 0.0) cost get_model_cost(gpt-4) print(f每1k token 费用: ${cost})7.3.2 多个返回值实际返回元组理论讲解Python函数可以返回多个值实际上这些值被封装成一个元组。调用方可以使用多个变量接收相当于元组解包。案例代码def get_model_info(model): 返回模型的名称上下文长度费用 if model gpt-4: return gpt-4, 8192, 0.03 elif model claude-3: return claude-3, 200000, 0.025 else: return unknown, 0, 0.0 name, limit, price get_model_info(gpt-4) print(f{name}: 上下文 {limit}, 价格 {price})7.3.3 返回 None理论讲解没有显式return或只写return的函数返回值是None。案例代码def print_warning(msg): print(f警告: {msg}) # 隐式返回 None result print_warning(温度过高) print(result) # 输出 None7.4 变量作用域理论讲解全局变量在函数外部定义的变量整个文件可访问。局部变量在函数内部定义的变量只在该函数内有效。函数内部可以读取全局变量但默认不能修改如果尝试赋值Python会创建新的局部变量。若要在函数内部修改全局变量需使用global关键字声明。但此做法不推荐应尽量通过参数和返回值传递数据。案例代码api_key sk-xxx # 全局变量 total_cost 0.0 def add_cost(amount): global total_cost # 声明要修改全局变量 total_cost amount def call_api(): retries 3 # 局部变量 print(f使用密钥: {api_key}) # 可以读取全局变量 call_api() add_cost(0.05) print(total_cost) # 输出 0.05 # print(retries) # 错误retries 在函数外不可见7.5 匿名函数lambda理论讲解lambda关键字用于创建简单的、单行的匿名函数。语法为lambda 参数: 表达式表达式的结果就是返回值。lambda 常用于需要一个简单函数但不想用def正式定义的场合例如作为sorted、filter、map等函数的参数。案例代码# 普通函数 def square(x): return x ** 2 # lambda 写法 square_lambda lambda x: x ** 2 print(square(5)) # 25 print(square_lambda(5)) # 25 # AI场景对模型列表按上下文长度排序 models [ {name: gpt-4, context: 8192}, {name: claude-3, context: 200000}, {name: gpt-3.5, context: 4096} ] sorted_models sorted(models, keylambda m: m[context]) for m in sorted_models: print(m[name], m[context]) # 输出 # gpt-3.5 4096 # gpt-4 8192 # claude-3 200000运行上述代码输出如下内容tianpengDESKTOP-4L1UF5S:~/my-ai-service$ poetry run python src/my_ai_service/loop.py 25 25 gpt-3.5 4096 gpt-4 8192 claude-3 200000 tianpengDESKTOP-4L1UF5S:~/my-ai-service$7.6 模块Module7.6.1 什么是模块理论讲解当代码越来越多时你会想把相关的函数、变量放到一个文件里这个文件就是一个模块.py文件。通过import语句可以在其他文件中使用该模块中的内容。模块是组织代码的基本单位也是实现代码复用的重要手段。案例代码创建ai_utils.py文件# ai_utils.py def count_tokens(text): return len(text.split()) def estimate_cost(model, tokens): prices {gpt-4: 0.03, gpt-3.5: 0.002} return tokens / 1000 * prices.get(model, 0) MODEL_LIST [gpt-4, gpt-3.5, claude-3]在另一个文件中导入使用# 方式1导入整个模块 import ai_utils tokens ai_utils.count_tokens(Hello AI) print(tokens) # 方式2导入特定函数/变量 from ai_utils import count_tokens, MODEL_LIST print(count_tokens(hi)) # 方式3给模块起别名 import ai_utils as ai print(ai.estimate_cost(gpt-4, 500)) # 方式4导入所有不推荐可能命名冲突 from ai_utils import *7.6.2if __name__ __main__的作用理论讲解当直接运行一个.py文件时其__name__属性会被设置为__main__当该文件被作为模块导入时__name__是模块名即文件名。因此if __name__ __main__:用于编写测试代码只有在直接运行该脚本时才会执行被导入时不会执行。案例代码在ai_utils.py末尾添加def main(): print(测试 count_tokens:, count_tokens(hello world)) print(测试 estimate_cost:, estimate_cost(gpt-4, 500)) if __name__ __main__: main()直接运行python ai_utils.py会执行测试在其他文件中import ai_utils则不会。7.6.3 Python 标准库常用模块理论讲解Python 自带丰富的标准库无需额外安装。AI 开发中常用的有json处理 JSON 数据API 请求/响应time时间相关重试延迟random随机数模拟采样math数学函数requests发送 HTTP 请求需安装但常与标准库一起介绍案例代码import json import time import random # 模拟 API 响应 response { id: chatcmpl-123, choices: [{message: {content: 你好}}] } json_str json.dumps(response, ensure_asciiFalse) print(json_str) # 随机温度 temp random.uniform(0.5, 1.5) print(f随机温度: {temp}) # 延迟1秒 time.sleep(1)运行上述代码输出如下的内容tianpengDESKTOP-4L1UF5S:~/my-ai-service$ poetry run python src/my_ai_service/loop.py {id: chatcmpl-123, choices: [{message: {content: 你好}}]} 随机温度: 0.9758707474342543 tianpengDESKTOP-4L1UF5S:~/my-ai-service$7.7 类型注解Type Hints理论讲解类型注解是 Python 3.5 引入的特性用于标注参数和返回值的类型。虽然运行时不会强制检查但可以帮助开发者理解代码IDE 也能提供更好的自动补全和错误提示。案例代码def call_llm(prompt: str, model: str gpt-4, temperature: float 0.7) - str: 返回模型的回复字符串 return f模拟回复: {prompt} # 调用时类型错误不会在运行时阻止但工具会有警告 result call_llm(你好, model123) # IDE 会提示类型不匹配7.8 异常处理基础7.8.1 什么是异常理论讲解异常是程序执行过程中发生的错误事件。当 Python 无法继续执行时它会“抛出”一个异常。如果没有被捕获程序会终止并打印错误信息traceback。在调用大模型 API、读取配置文件、处理用户输入时各种意外情况都可能发生网络中断、JSON 解析失败、文件不存在等。异常处理就是让程序在遇到错误时能够“优雅地恢复”或给出友好提示而不是直接崩溃。7.8.2 常见内置异常异常名称发生场景AI 示例FileNotFoundError文件不存在读取不存在的 config.jsonJSONDecodeErrorJSON 解析失败API 返回非 JSON 格式KeyError字典中键不存在response[choices][0]缺少字段IndexError列表索引越界messages[0]但列表为空TypeError类型不匹配字符串和整数相加ValueError值不符合预期int(abc)ConnectionError网络连接问题无法连接到 API 服务器TimeoutError操作超时API 请求超时7.8.3 try-except 基础语法理论讲解使用try-except捕获异常。基本结构try: # 可能抛出异常的代码 except 异常类型: # 发生该异常时执行的代码案例代码# 没有异常处理的脆弱程序 # with open(api_key.txt, r) as f: # api_key f.read().strip() # 文件不存在会崩溃 # 有异常处理的健壮程序 try: with open(api_key.txt, r) as f: api_key f.read().strip() print(f密钥是: {api_key}) except FileNotFoundError: print(错误: 找不到 api_key.txt 文件请检查。) api_key default_key7.9 异常处理高级用法7.9.1 捕获多种异常理论讲解一个try块可能抛出多种不同类型的异常你可以分别捕获并处理。多个except块可以针对不同异常编写不同的处理逻辑。Exception是几乎所有异常的基类可以放在最后作为“兜底”捕获其他未指定的异常。案例代码def call_model(prompt, config): try: if config.get(bad) True: raise ValueError(温度设置无效) result {choices: [{message: {content: 回复内容}}]} return result[choices][0][message][content] except KeyError: print(错误: 返回数据结构异常缺少必要字段) return 默认回复 except ValueError as e: print(f参数错误: {e}) return 参数无效无法调用 except Exception as e: print(f未知错误: {e}) return 系统错误 print(call_model(hello, {})) print(call_model(hello, {bad: True}))7.9.2 else 子句理论讲解else块中的代码只有在try块没有抛出任何异常时才会执行。通常用于放置那些依赖于try块成功执行的代码。案例代码try: with open(config.json, r) as f: config json.load(f) except FileNotFoundError: print(配置文件不存在使用默认配置) config {model: gpt-3.5, temperature: 0.7} else: print(成功加载配置文件并进行了额外处理) config[loaded_at] now7.9.3 finally 子句理论讲解finally块中的代码无论是否发生异常都会执行非常适合用来释放资源如关闭文件、断开网络连接。案例代码def api_call_with_cleanup(): try: print(发起 API 调用...) raise ConnectionError(网络中断) except ConnectionError as e: print(f捕获到错误: {e}) return 错误返回 finally: print(关闭连接清理资源一定会执行) result api_call_with_cleanup() # 输出 # 发起 API 调用... # 捕获到错误: 网络中断 # 关闭连接清理资源一定会执行7.9.4 主动抛出异常raise理论讲解使用raise关键字可以主动抛出异常。当检测到不合理的情况时自己抛出异常比让 Python 被动发现更清晰。案例代码def set_temperature(value): if not isinstance(value, (int, float)): raise TypeError(温度必须是数字) if value 0 or value 2: raise ValueError(温度必须在0到2之间) print(f温度设置为 {value}) try: set_temperature(2.5) # 超出范围 except ValueError as e: print(f参数错误: {e})7.9.5 自定义异常理论讲解通过继承Exception类可以定义自己的异常。自定义异常能够更精确地表达业务错误便于在复杂系统中区分不同类型的错误。案例代码class ModelNotSupportedError(Exception): 自定义异常模型不支持 pass def call_model(model_name): supported [gpt-4, claude-3] if model_name not in supported: raise ModelNotSupportedError(f模型 {model_name} 不在支持列表中) return f正在调用 {model_name} try: call_model(llama-2) except ModelNotSupportedError as e: print(f自定义异常捕获{e})7.10 综合实战健壮的 LLM 调用模块理论讲解将本章所有知识整合成一个完整的模块simple_llm.py包含参数校验、成本估算、模拟调用、异常处理、重试机制等功能。案例代码# simple_llm.py import json import time import random # 可用的模型及其配置 _MODELS { gpt-4: {context: 8192, cost_per_1k: 0.03}, gpt-3.5: {context: 4096, cost_per_1k: 0.002}, claude-3: {context: 200000, cost_per_1k: 0.025}, } # 自定义异常 class APITemporaryError(Exception): 临时性错误可重试 pass class APIPermanentError(Exception): 永久性错误不应重试 pass def validate_model(model: str) - bool: 检查模型是否支持 return model in _MODELS def get_model_context(model: str) - int: 返回模型的最大上下文长度 return _MODELS.get(model, {}).get(context, 0) def estimate_cost(model: str, input_tokens: int, output_tokens: int) - float: 估算本次调用的费用美元 if model not in _MODELS: return 0.0 price _MODELS[model][cost_per_1k] total_tokens input_tokens output_tokens return total_tokens / 1000 * price def simulate_call(prompt: str, model: str gpt-3.5, temperature: float 0.7) - dict: 模拟调用大模型 API if not validate_model(model): return {error: f不支持的模型 {model}} # 模拟 token 计数粗略估计 input_tokens len(prompt) // 4 output_text f这是 {model} 对 {prompt[:30]}... 的模拟回复。 output_tokens len(output_text) // 4 cost estimate_cost(model, input_tokens, output_tokens) return { model: model, temperature: temperature, reply: output_text, input_tokens: input_tokens, output_tokens: output_tokens, cost_usd: round(cost, 6) } def robust_llm_call(prompt: str, max_retries: int 3): 带有重试和异常处理的健壮LLM调用 for attempt in range(1, max_retries 1): try: print(f尝试 {attempt}/{max_retries}...) # 模拟可能发生的各种错误 rand random.random() if rand 0.1: raise FileNotFoundError(配置文件缺失) elif rand 0.3: raise ConnectionError(网络连接失败) elif rand 0.5: raise json.JSONDecodeError(Expecting value, doc, 0) elif rand 0.6: raise ValueError(无效的温度参数) # 成功情况 return f成功回复: {prompt} except FileNotFoundError as e: print(f永久错误无法恢复: {e}) raise APIPermanentError(缺少必要文件请检查配置) from e except ValueError as e: print(f参数错误无法恢复: {e}) raise APIPermanentError(参数无效请修改请求) from e except (ConnectionError, json.JSONDecodeError) as e: print(f临时错误: {type(e).__name__}: {e}) if attempt max_retries: raise APITemporaryError(重试次数用尽请稍后再试) from e wait 2 ** attempt # 指数退避 print(f等待 {wait} 秒后重试...) time.sleep(wait) except Exception as e: print(f未知错误: {e}) raise APITemporaryError(f未知错误: {e}) from e return None # 直接运行脚本时执行测试 if __name__ __main__: print(测试 simple_llm 模块) result simulate_call(请解释什么是注意力机制, modelgpt-4, temperature0.8) for key, value in result.items(): print(f{key}: {value}) print(\n测试健壮调用随机错误) for i in range(5): print(f\n--- 第 {i1} 次测试 ---) try: res robust_llm_call(你好AI) print(res) except APIPermanentError as e: print(f永久性失败: {e}) except APITemporaryError as e: print(f临时性失败: {e}) time.sleep(1)在其他脚本中使用的示例import simple_llm response simple_llm.simulate_call(写一首关于 AI 的诗, claude-3) print(f回复: {response[reply]}) print(f消耗 token: 输入 {response[input_tokens]}, 输出 {response[output_tokens]}) print(f费用: ${response[cost_usd]})7.11 本章小结概念作用AI 开发示例函数定义def关键字def count_tokens(text):位置参数按顺序匹配send_prompt(model, prompt, temp)默认参数提供默认值call_llm(prompt, modelgpt-4)关键字参数指定参数名调用call_llm(prompt你好, modelgpt-4)*args/**kwargs接收任意数量参数封装函数日志返回值return结果返回 token 数、费用等作用域局部/全局变量global修改全局配置lambda匿名单行函数sorted(models, keylambda x: x[context])模块.py文件import json,from ai_utils import count_tokensif __name__ __main__区分直接运行和导入模块测试代码类型注解标注参数/返回类型def f(name: str) - list:try-except捕获异常文件不存在时使用默认配置多重except区分不同异常分别处理网络错误、超时、JSON 错误else无异常时执行加载配置成功后打印日志finally无论异常与否都执行关闭文件或网络连接raise主动抛出异常校验参数不合法时抛出ValueError自定义异常精确表达业务错误ModelNotSupportedError重试机制处理临时性错误API 限流或网络抖动时自动重试