Python 基础课:函数与模块
学习目标
今天结束后,你能够:
- 定义和调用函数,实现代码复用
- 理解参数传递(位置参数、关键字参数、默认参数)
- 使用返回值获取函数执行结果
- 理解变量作用域(局部、全局)
- 使用 lambda 表达式创建简单函数
- 导入和使用标准库模块
- 创建自定义模块并在多文件项目中使用
- 使用 pip 管理第三方包
函数
函数是把重复使用的代码封装起来,给它一个名字,需要时调用。
为什么需要函数
没有函数时,代码会重复:
# 计算三次 token 费用
tokens1 = 1000
cost1 = (tokens1 / 1000) * 0.03
print(f"费用1: ${cost1:.4f}")
tokens2 = 1500
cost2 = (tokens2 / 1000) * 0.03
print(f"费用2: ${cost2:.4f}")
tokens3 = 2000
cost3 = (tokens3 / 1000) * 0.03
print(f"费用3: ${cost3:.4f}")
有了函数,代码简洁:
def calculate_cost(tokens):
"""计算 token 费用"""
return (tokens / 1000) * 0.03
print(f"费用1: ${calculate_cost(1000):.4f}")
print(f"费用2: ${calculate_cost(1500):.4f}")
print(f"费用3: ${calculate_cost(2000):.4f}")
输出:
费用1: $0.0300
费用2: $0.0450
费用3: $0.0600
函数定义
基本语法:
def 函数名(参数1, 参数2):
"""函数说明文档(可选)"""
# 函数体
return 返回值
示例:
def greet(name):
"""向用户打招呼"""
message = f"你好,{name}!"
return message
result = greet("小明")
print(result) # 你好,小明!
函数命名规范:
- 使用小写字母和下划线
- 名字要表达函数的功能
- 动词开头:
get_user_info()、calculate_cost()、build_prompt()
函数说明文档(docstring):
三引号字符串,写在函数开头,说明函数的功能。
def calculate_cost(tokens, price_per_1k=0.03):
"""
计算 token 费用
参数:
tokens: token 数量
price_per_1k: 每 1K tokens 的价格,默认 0.03
返回:
float: 总费用
"""
return (tokens / 1000) * price_per_1k
参数传递
位置参数
按照顺序传递参数。
def build_message(role, content):
return {"role": role, "content": content}
msg = build_message("user", "你好")
print(msg) # {'role': 'user', 'content': '你好'}
关键字参数
使用参数名传递,不依赖顺序。
msg = build_message(content="你好", role="user")
print(msg) # {'role': 'user', 'content': '你好'}
默认参数
参数可以有默认值,调用时可以不传。
def call_api(model="gpt-3.5-turbo", temperature=0.7):
"""调用 AI 模型"""
print(f"模型: {model}, 温度: {temperature}")
call_api() # 使用默认值
call_api("gpt-4") # 只改 model
call_api(temperature=0.9) # 只改 temperature
call_api("gpt-4", 0.9) # 全部指定
输出:
模型: gpt-3.5-turbo, 温度: 0.7
模型: gpt-4, 温度: 0.7
模型: gpt-3.5-turbo, 温度: 0.9
模型: gpt-4, 温度: 0.9
注意:默认参数必须放在位置参数后面
# 错误
def func(a=1, b): # SyntaxError
pass
# 正确
def func(b, a=1):
pass
返回值
return 的作用:
- 返回结果给调用者
- 结束函数执行
def add(a, b):
return a + b
result = add(3, 5)
print(result) # 8
没有 return 时返回 None
def print_hello():
print("Hello")
result = print_hello()
print(result) # None
多返回值
实际上是返回一个元组。
def get_model_info():
name = "gpt-4"
price = 0.03
max_tokens = 8192
return name, price, max_tokens # 返回元组
model, price, tokens = get_model_info() # 元组解包
print(f"{model}: ${price}, {tokens} tokens")
输出:
gpt-4: $0.03, 8192 tokens
提前返回
用于简化逻辑。
def validate_temperature(temp):
"""验证温度参数"""
if temp < 0 or temp > 2:
return False # 提前返回
return True
print(validate_temperature(0.7)) # True
print(validate_temperature(3.0)) # False
AI 场景示例
def build_prompt(system_msg, user_msg):
"""构造完整的 prompt"""
return f"[System]\n{system_msg}\n\n[User]\n{user_msg}"
def count_tokens(text):
"""简单的 token 估算(字符数 ÷ 4)"""
return len(text) // 4
def estimate_cost(text, price_per_1k=0.03):
"""估算文本的费用"""
tokens = count_tokens(text)
cost = (tokens / 1000) * price_per_1k
return tokens, cost
# 使用函数
system = "你是一个 Python 助手"
user = "如何读取文件?"
prompt = build_prompt(system, user)
tokens, cost = estimate_cost(prompt)
print(f"Prompt:\n{prompt}")
print(f"\nToken 数: {tokens}")
print(f"预估费用: ${cost:.4f}")
课堂练习
消息构造器
编写函数 create_message(role, content),返回消息字典。
编写函数 add_message(messages, role, content),向消息列表添加消息。
参考答案:
def create_message(role, content):
"""创建消息字典"""
return {"role": role, "content": content}
def add_message(messages, role, content):
"""向消息列表添加消息"""
msg = create_message(role, content)
messages.append(msg)
# 测试
messages = []
add_message(messages, "system", "你是助手")
add_message(messages, "user", "你好")
add_message(messages, "assistant", "你好!")
for msg in messages:
print(f"[{msg['role']}] {msg['content']}")
输出:
[system] 你是助手
[user] 你好
[assistant] 你好!
变量作用域
变量的作用域决定了它在哪里可以被访问。
局部变量
在函数内部定义的变量,只能在函数内部使用。
def calculate():
result = 100 # 局部变量
print(result)
calculate() # 100
# print(result) # NameError: name 'result' is not defined
全局变量
在函数外部定义的变量,可以在函数内部读取。
MODEL = "gpt-4" # 全局变量
def get_model():
print(MODEL) # 可以读取全局变量
get_model() # gpt-4
修改全局变量
如果要在函数内修改全局变量,需要使用 global 关键字。
count = 0 # 全局变量
def increment():
global count # 声明要修改全局变量
count += 1
increment()
increment()
print(count) # 2
但不推荐这样做,容易导致代码混乱。更好的方式是通过参数和返回值:
count = 0
def increment(n):
"""返回递增后的值"""
return n + 1
count = increment(count)
count = increment(count)
print(count) # 2
作用域规则(LEGB)
Python 查找变量的顺序:
- Local(局部):函数内部
- Enclosing(外层):嵌套函数的外层
- Global(全局):模块级别
- Built-in(内置):Python 内置
x = "global" # 全局
def outer():
x = "outer" # 外层
def inner():
x = "inner" # 局部
print(x)
inner()
print(x)
outer()
print(x)
输出:
inner
outer
global
lambda 表达式
lambda 是创建简单函数的快捷方式,适合一次性使用的简单函数。
基本语法
lambda 参数: 表达式
普通函数:
def add(a, b):
return a + b
print(add(3, 5)) # 8
lambda 表达式:
add = lambda a, b: a + b
print(add(3, 5)) # 8
使用场景
1. 作为参数传递
# 对列表排序
models = [
{"name": "gpt-3.5", "price": 0.002},
{"name": "gpt-4", "price": 0.03},
{"name": "claude", "price": 0.015}
]
# 按价格排序
models.sort(key=lambda x: x["price"])
for m in models:
print(f"{m['name']}: ${m['price']}")
输出:
gpt-3.5: $0.002
claude: $0.015
gpt-4: $0.03
2. 简单的数据转换
# 将列表中的字符串转为大写
words = ["hello", "world", "python"]
upper_words = list(map(lambda x: x.upper(), words))
print(upper_words) # ['HELLO', 'WORLD', 'PYTHON']
# 过滤偶数
numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6]
注意:
- lambda 只能包含一个表达式
- 不能包含多行语句
- 不能包含 if-elif-else(但可以用三元表达式)
- 复杂逻辑用普通函数
# lambda 中使用三元表达式
get_grade = lambda score: "及格" if score >= 60 else "不及格"
print(get_grade(75)) # 及格
print(get_grade(45)) # 不及格
装饰器
装饰器是 Python 的重要特性,用于在不修改函数代码的情况下,为函数添加额外功能。
为什么需要装饰器
在 AI 开发中经常需要:
- 记录函数调用日志
- 重试失败的 API 调用
- 缓存结果
- 计算函数执行时间
装饰器让这些功能可以复用,不用重复写代码。
函数是对象
在 Python 中,函数也是对象,可以作为参数传递。
def greet():
return "Hello"
# 函数赋值给变量
func = greet
print(func()) # Hello
# 函数作为参数
def call_function(f):
return f()
print(call_function(greet)) # Hello
基础装饰器
最简单的装饰器:
def my_decorator(func):
"""装饰器函数"""
def wrapper():
print("函数执行前")
result = func() # 调用原函数
print("函数执行后")
return result
return wrapper
# 使用装饰器
@my_decorator
def say_hello():
print("Hello!")
return "完成"
# 调用
say_hello()
输出:
函数执行前
Hello!
函数执行后
@my_decorator 等价于:
def say_hello():
print("Hello!")
say_hello = my_decorator(say_hello)
带参数的装饰器
装饰有参数的函数:
def log_decorator(func):
def wrapper(*args, **kwargs): # 接收任意参数
print(f"调用函数: {func.__name__}")
print(f"参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"返回值: {result}")
return result
return wrapper
@log_decorator
def add(a, b):
return a + b
@log_decorator
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
# 使用
add(3, 5)
greet("Alice", greeting="Hi")
输出:
调用函数: add
参数: args=(3, 5), kwargs={}
返回值: 8
调用函数: greet
参数: args=('Alice',), kwargs={'greeting': 'Hi'}
返回值: Hi, Alice!
带参数的装饰器本身
def retry(max_attempts=3):
"""重试装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"尝试 {attempt + 1} 失败: {e}")
if attempt == max_attempts - 1:
raise
return wrapper
return decorator
@retry(max_attempts=3)
def unstable_api_call():
"""模拟不稳定的 API 调用"""
import random
if random.random() < 0.7:
raise ValueError("API 调用失败")
return "成功"
# 使用
result = unstable_api_call()
print(result)
AI 场景示例
1. API 重试装饰器
import time
def retry_on_failure(max_attempts=3, delay=1):
"""API 调用失败时重试"""
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第 {attempt + 1} 次失败: {e}")
if attempt < max_attempts - 1:
time.sleep(delay)
else:
raise
return wrapper
return decorator
@retry_on_failure(max_attempts=3, delay=2)
def call_openai_api(prompt):
"""调用 OpenAI API"""
# 模拟 API 调用
import random
if random.random() < 0.5:
raise ConnectionError("网络错误")
return "AI 的回复"
# 使用
response = call_openai_api("你好")
print(response)
2. 计时装饰器
import time
def timer(func):
"""计算函数执行时间"""
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间: {end - start:.2f} 秒")
return result
return wrapper
@timer
def process_large_text(text):
"""处理大文本"""
time.sleep(1) # 模拟耗时操作
return len(text)
# 使用
process_large_text("长文本" * 1000)
3. 多个装饰器组合
@retry_on_failure(max_attempts=3)
@timer
def call_ai_with_retry(prompt):
"""带重试和计时的 API 调用"""
# 模拟 API 调用
time.sleep(0.5)
return "AI 回复"
# 执行顺序:retry -> timer -> 函数本身
result = call_ai_with_retry("你好")
常用装饰器模式
缓存结果:
def cache(func):
"""缓存函数结果"""
cached = {}
def wrapper(*args):
if args not in cached:
cached[args] = func(*args)
return cached[args]
return wrapper
@cache
def expensive_computation(n):
"""耗时计算"""
print(f"计算 {n}...")
time.sleep(1)
return n * n
# 第一次调用:计算
print(expensive_computation(5)) # 计算 5... 25
# 第二次调用:从缓存读取
print(expensive_computation(5)) # 25(直接返回,不打印"计算")
课堂练习
日志装饰器
创建一个装饰器,记录函数的调用信息。
参考答案:
import logging
logging.basicConfig(level=logging.INFO)
def log_calls(func):
"""记录函数调用日志"""
def wrapper(*args, **kwargs):
logging.info(f"调用 {func.__name__},参数: {args}, {kwargs}")
result = func(*args, **kwargs)
logging.info(f"{func.__name__} 返回: {result}")
return result
return wrapper
@log_calls
def send_message(role, content):
"""发送消息"""
return {"role": role, "content": content}
# 测试
msg = send_message("user", "你好")
输出:
INFO:root:调用 send_message,参数: ('user', '你好'), {}
INFO:root:send_message 返回: {'role': 'user', 'content': '你好'}
模块与导入
模块是包含 Python 代码的 .py 文件,可以被其他文件导入使用。
为什么需要模块
随着项目变大,把所有代码写在一个文件会很混乱。模块帮助我们:
- 组织代码
- 避免重复
- 便于维护
- 便于团队协作
导入标准库模块
Python 自带了很多有用的模块(标准库),可以直接导入使用。
import 模块名
import math # 导入 math 模块
print(math.pi) # 3.141592653589793
print(math.sqrt(16)) # 4.0
print(math.ceil(3.2)) # 4(向上取整)
print(math.floor(3.8)) # 3(向下取整)
from 模块 import 函数
只导入需要的部分。
from math import pi, sqrt
print(pi) # 3.141592653589793
print(sqrt(16)) # 4.0
# print(math.pi) # NameError: name 'math' is not defined
import 模块 as 别名
给模块起个短名字。
import datetime as dt
now = dt.datetime.now() # 使用别名 dt
print(now)
常用标准库模块
os - 操作系统相关
import os
# 获取环境变量
api_key = os.getenv("OPENAI_API_KEY")
print(api_key)
# 获取当前工作目录
print(os.getcwd())
json - JSON 处理
import json
# Python 字典转 JSON 字符串
data = {"name": "gpt-4", "price": 0.03}
json_str = json.dumps(data)
print(json_str) # {"name": "gpt-4", "price": 0.03}
# JSON 字符串转 Python 字典
obj = json.loads(json_str)
print(obj["name"]) # gpt-4
datetime - 日期时间处理
datetime 模块用于处理日期和时间,在 AI 开发中非常常用。
from datetime import datetime, timedelta
# 获取当前时间
now = datetime.now()
print(now) # 2024-06-11 15:30:45.123456
# 格式化输出
print(now.strftime("%Y-%m-%d %H:%M:%S")) # 2024-06-11 15:30:45
# ISO 格式(API 常用)
print(now.isoformat()) # 2024-06-11T15:30:45.123456
# 创建指定时间
specific_time = datetime(2024, 6, 11, 15, 30, 0)
print(specific_time)
# 时间计算
tomorrow = now + timedelta(days=1)
one_hour_ago = now - timedelta(hours=1)
print(f"明天: {tomorrow}")
print(f"一小时前: {one_hour_ago}")
# 解析时间字符串
time_str = "2024-06-11 15:30:00"
parsed_time = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
print(parsed_time)
常用格式化代码:
%Y- 4位年份(2024)%m- 2位月份(01-12)%d- 2位日期(01-31)%H- 24小时制小时(00-23)%M- 分钟(00-59)%S- 秒(00-59)
AI 场景示例:
from datetime import datetime
class ChatSession:
"""对话会话"""
def __init__(self):
self.messages = []
self.created_at = datetime.now()
def add_message(self, role, content):
"""添加消息(带时间戳)"""
message = {
"role": role,
"content": content,
"timestamp": datetime.now().isoformat()
}
self.messages.append(message)
def session_duration(self):
"""会话持续时间"""
duration = datetime.now() - self.created_at
return duration.total_seconds()
def get_session_id(self):
"""生成会话ID"""
return self.created_at.strftime("%Y%m%d_%H%M%S")
# 使用
session = ChatSession()
session.add_message("user", "你好")
print(f"会话ID: {session.get_session_id()}")
print(f"持续时间: {session.session_duration()} 秒")
time - 时间相关
import time
# 暂停执行
print("开始")
time.sleep(2) # 暂停 2 秒
print("结束")
# 获取时间戳
timestamp = time.time()
print(timestamp) # 1718096445.123456
# 计算执行时间
start = time.time()
# 执行一些操作
time.sleep(1)
end = time.time()
print(f"耗时: {end - start:.2f} 秒")
random - 随机数
import random
# 随机整数
print(random.randint(1, 10)) # 1-10 之间的随机整数
# 随机选择
models = ["gpt-3.5", "gpt-4", "claude"]
print(random.choice(models))
# 打乱列表
random.shuffle(models)
print(models)
自定义模块
创建模块
创建一个文件就是创建一个模块。
示例:创建 utils.py
# utils.py
"""工具函数模块"""
def count_tokens(text):
"""估算 token 数量"""
return len(text) // 4
def calculate_cost(tokens, price_per_1k=0.03):
"""计算费用"""
return (tokens / 1000) * price_per_1k
# 模块级常量
DEFAULT_MODEL = "gpt-3.5-turbo"
在其他文件中导入
# main.py
import utils
text = "这是一段测试文本"
tokens = utils.count_tokens(text)
cost = utils.calculate_cost(tokens)
print(f"Token 数: {tokens}")
print(f"费用: ${cost:.4f}")
print(f"默认模型: {utils.DEFAULT_MODEL}")
或者只导入需要的函数
# main.py
from utils import count_tokens, calculate_cost
text = "这是一段测试文本"
tokens = count_tokens(text)
cost = calculate_cost(tokens)
print(f"Token 数: {tokens}, 费用: ${cost:.4f}")
__name__ == "__main__"
这是 Python 中最常见的代码模式,用于区分模块是被导入还是直接运行。
为什么需要这个判断
在 Python 中,每个模块都有一个内置变量 __name__:
- 当模块被直接运行时,
__name__的值是"__main__" - 当模块被导入时,
__name__的值是模块名(文件名)
这个机制让我们可以:
- 在模块中写测试代码
- 既能作为模块被导入,又能独立运行测试
基础示例
# utils.py
def count_tokens(text):
"""计算token数"""
return len(text) // 4
def build_prompt(system, user):
"""构建prompt"""
return f"[System]\n{system}\n\n[User]\n{user}"
# 这部分代码只在直接运行 utils.py 时执行
if __name__ == "__main__":
print("=== 测试工具函数 ===")
# 测试 count_tokens
test_text = "Hello Python World"
tokens = count_tokens(test_text)
print(f"Token数: {tokens}")
# 测试 build_prompt
prompt = build_prompt("你是助手", "你好")
print(f"\nPrompt:\n{prompt}")
运行方式1:直接运行
python utils.py
输出:
=== 测试工具函数 ===
Token数: 4
Prompt:
[System]
你是助手
[User]
你好
运行方式2:被导入
# main.py
import utils
# 只导入函数,测试代码不会执行
tokens = utils.count_tokens("Hello")
print(tokens) # 1
AI 场景示例
# message.py
"""消息管理模块"""
def create_message(role, content):
"""创建消息字典"""
return {"role": role, "content": content}
def add_message(messages, role, content):
"""向消息列表添加消息"""
msg = create_message(role, content)
messages.append(msg)
def print_messages(messages):
"""打印所有消息"""
for msg in messages:
print(f"[{msg['role']}] {msg['content']}")
# 测试代码:只在直接运行时执行
if __name__ == "__main__":
print("=== 测试消息管理模块 ===\n")
# 创建测试消息列表
messages = []
add_message(messages, "system", "你是一个Python助手")
add_message(messages, "user", "如何读取文件?")
add_message(messages, "assistant", "使用open()函数...")
# 打印消息
print_messages(messages)
# 统计
print(f"\n总消息数: {len(messages)}")
常见使用场景
1. 模块测试
# api_client.py
def call_api(prompt):
"""调用API(实际项目中)"""
# 实际API调用代码
pass
if __name__ == "__main__":
# 测试API调用
result = call_api("测试prompt")
print(result)
2. 命令行工具
# tool.py
def process_file(filename):
"""处理文件"""
with open(filename) as f:
return f.read()
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
filename = sys.argv[1]
result = process_file(filename)
print(result)
else:
print("用法: python tool.py <文件名>")
3. 示例代码
# config.py
API_KEY = "your-api-key"
MODEL = "gpt-3.5-turbo"
if __name__ == "__main__":
# 显示配置示例
print("配置示例:")
print(f"API_KEY: {API_KEY}")
print(f"MODEL: {MODEL}")
课堂练习
为 Day3 早期创建的工具函数添加测试代码。
参考答案:
# token_utils.py
"""Token 计算工具"""
def count_tokens(text):
"""估算token数"""
return len(text) // 4
def estimate_cost(text, price_per_1k=0.03):
"""估算费用"""
tokens = count_tokens(text)
return (tokens / 1000) * price_per_1k
if __name__ == "__main__":
print("=== Token 工具测试 ===\n")
# 测试数据
test_texts = [
"Hello",
"这是一段测试文本",
"Python is awesome!" * 10
]
for text in test_texts:
tokens = count_tokens(text)
cost = estimate_cost(text)
print(f"文本: {text[:20]}...")
print(f"Token数: {tokens}")
print(f"预估费用: ${cost:.4f}\n")
多文件项目组织
一个典型的 AI 项目结构:
my_ai_project/
├── main.py # 主程序入口
├── config.py # 配置
├── utils.py # 工具函数
├── prompt.py # Prompt 相关
├── message.py # 消息管理
└── data/ # 数据目录
└── chat_log.json
config.py - 配置
# config.py
"""项目配置"""
DEFAULT_MODEL = "gpt-3.5-turbo"
DEFAULT_TEMPERATURE = 0.7
MAX_TOKENS = 2000
utils.py - 工具函数
# utils.py
"""工具函数"""
def count_tokens(text):
return len(text) // 4
def calculate_cost(tokens, price_per_1k=0.03):
return (tokens / 1000) * price_per_1k
message.py - 消息管理
# message.py
"""消息管理"""
def create_message(role, content):
return {"role": role, "content": content}
def add_message(messages, role, content):
msg = create_message(role, content)
messages.append(msg)
return messages
main.py - 主程序
# main.py
"""主程序"""
import config
from utils import count_tokens, calculate_cost
from message import create_message, add_message
def main():
print(f"默认模型: {config.DEFAULT_MODEL}")
messages = []
add_message(messages, "system", "你是助手")
add_message(messages, "user", "你好")
for msg in messages:
print(f"[{msg['role']}] {msg['content']}")
if __name__ == "__main__":
main()
包管理
什么是包(Package)
当项目更大时,可以用文件夹组织模块,这个文件夹就叫”包”。
my_project/
├── main.py
└── ai_tools/ # 包
├── __init__.py # 必须有这个文件,可以是空的
├── prompt.py
└── message.py
导入包中的模块
# main.py
from ai_tools import prompt
from ai_tools import message
# 或
from ai_tools.prompt import build_prompt
from ai_tools.message import create_message
pip - 包管理工具
pip 是 Python 的包管理工具,用于安装第三方库。
安装包
pip install requests
查看已安装的包
pip list
卸载包
pip uninstall requests
安装指定版本
pip install requests==2.28.0
requirements.txt
用于记录项目依赖,方便在其他环境安装。
生成 requirements.txt
pip freeze > requirements.txt
内容示例:
requests==2.28.0
python-dotenv==1.0.0
安装依赖
pip install -r requirements.txt
虚拟环境
虚拟环境让每个项目有独立的依赖,避免冲突。
创建虚拟环境
python -m venv .venv
激活虚拟环境
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
退出虚拟环境
deactivate
PyCharm 会自动管理虚拟环境,一般不需要手动操作。
当天作业
工具函数库
创建一个 ai_utils.py 模块,包含以下函数:
count_tokens(text)- 估算 token 数calculate_cost(tokens, price_per_1k)- 计算费用truncate_text(text, max_length)- 截断文本到指定长度format_messages(messages)- 格式化消息列表为字符串
然后在 main.py 中导入并测试。
参考答案:
# ai_utils.py
"""AI 工具函数库"""
def count_tokens(text):
"""估算 token 数量(字符数 ÷ 4)"""
return len(text) // 4
def calculate_cost(tokens, price_per_1k=0.03):
"""计算费用"""
return (tokens / 1000) * price_per_1k
def truncate_text(text, max_length):
"""截断文本"""
if len(text) <= max_length:
return text
return text[:max_length] + "..."
def format_messages(messages):
"""格式化消息列表"""
result = []
for msg in messages:
line = f"[{msg['role']}] {msg['content']}"
result.append(line)
return "\n".join(result) # join() 将列表元素用换行符连接
if __name__ == "__main__":
# 测试代码
print("测试 count_tokens:")
print(count_tokens("Hello Python"))
print("\n测试 calculate_cost:")
print(f"${calculate_cost(1000):.4f}")
print("\n测试 truncate_text:")
print(truncate_text("这是一段很长的文本", 5))
# main.py
"""主程序"""
from ai_utils import count_tokens, calculate_cost, format_messages
messages = [
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!有什么可以帮你?"}
]
# 格式化输出
print("=== 消息列表 ===")
print(format_messages(messages))
# 统计 token
total_text = " ".join([msg["content"] for msg in messages])
tokens = count_tokens(total_text)
cost = calculate_cost(tokens)
print(f"\n=== 统计 ===")
print(f"总 Token 数: {tokens}")
print(f"预估费用: ${cost:.4f}")
输出:
=== 消息列表 ===
[system] 你是助手
[user] 你好
[assistant] 你好!有什么可以帮你?
=== 统计 ===
总 Token 数: 7
预估费用: $0.0002
Prompt 构造器模块
创建 prompt_builder.py 模块,实现以下功能:
build_system_prompt(role_desc)- 构造系统 promptbuild_user_prompt(task, context)- 构造用户 promptbuild_full_prompt(system, user)- 组合完整 prompt
参考答案:
# prompt_builder.py
"""Prompt 构造器"""
def build_system_prompt(role_desc):
"""构造系统 prompt"""
return f"你是一个{role_desc}。请按照要求回答问题。"
def build_user_prompt(task, context=""):
"""构造用户 prompt"""
if context:
return f"背景信息:{context}\n\n任务:{task}"
return f"任务:{task}"
def build_full_prompt(system, user):
"""组合完整 prompt"""
return f"[System]\n{system}\n\n[User]\n{user}"
if __name__ == "__main__":
# 测试
system = build_system_prompt("Python 编程助手")
user = build_user_prompt("解释什么是函数", "初学者正在学习 Python")
full = build_full_prompt(system, user)
print(full)
输出:
[System]
你是一个Python 编程助手。请按照要求回答问题。
[User]
背景信息:初学者正在学习 Python
任务:解释什么是函数
消息管理器
创建一个完整的消息管理模块 message_manager.py,包含:
- 创建消息
- 添加消息
- 获取最近 N 条消息
- 统计各角色消息数量
参考答案:
# message_manager.py
"""消息管理器"""
def create_message(role, content):
"""创建消息"""
return {"role": role, "content": content}
def add_message(messages, role, content):
"""添加消息"""
msg = create_message(role, content)
messages.append(msg)
return messages
def get_recent_messages(messages, n=5):
"""获取最近 N 条消息"""
return messages[-n:] # 负索引切片,取最后 n 个
def count_by_role(messages):
"""统计各角色消息数量"""
counts = {}
for msg in messages:
role = msg["role"]
counts[role] = counts.get(role, 0) + 1
return counts
def filter_by_role(messages, role):
"""筛选指定角色的消息"""
return [msg for msg in messages if msg["role"] == role]
if __name__ == "__main__":
# 测试
messages = []
add_message(messages, "system", "你是助手")
add_message(messages, "user", "你好")
add_message(messages, "assistant", "你好!")
add_message(messages, "user", "天气如何")
add_message(messages, "assistant", "今天天气不错")
print("最近 3 条消息:")
for msg in get_recent_messages(messages, 3):
print(f" [{msg['role']}] {msg['content']}")
print("\n消息统计:")
counts = count_by_role(messages)
for role, count in counts.items():
print(f" {role}: {count} 条")
输出:
最近 3 条消息:
[user] 天气如何
[assistant] 今天天气不错
消息统计:
system: 1 条
user: 2 条
assistant: 2 条
配置管理器
创建 config.py 存储项目配置,main.py 读取并使用。
参考答案:
# config.py
"""项目配置"""
# 模型配置
DEFAULT_MODEL = "gpt-3.5-turbo"
DEFAULT_TEMPERATURE = 0.7
MAX_TOKENS = 2000
# 价格配置(每 1K tokens)
PRICING = {
"gpt-3.5-turbo": 0.002,
"gpt-4": 0.03,
"claude-3": 0.015
}
# 系统消息
SYSTEM_MESSAGES = {
"helper": "你是一个有帮助的AI助手",
"coder": "你是一个专业的编程助手",
"translator": "你是一个专业的翻译助手"
}
def get_price(model):
"""获取模型价格"""
return PRICING.get(model, 0.03) # 默认 0.03
def get_system_message(role):
"""获取系统消息"""
return SYSTEM_MESSAGES.get(role, SYSTEM_MESSAGES["helper"])
# main.py
"""主程序"""
import config
def show_config():
print("=== 当前配置 ===")
print(f"默认模型: {config.DEFAULT_MODEL}")
print(f"温度: {config.DEFAULT_TEMPERATURE}")
print(f"最大 tokens: {config.MAX_TOKENS}")
print("\n=== 价格表 ===")
for model, price in config.PRICING.items():
print(f"{model}: ${price}/1K tokens")
print("\n=== 系统消息 ===")
for role in ["helper", "coder", "translator"]:
msg = config.get_system_message(role)
print(f"{role}: {msg}")
if __name__ == "__main__":
show_config()
输出:
=== 当前配置 ===
默认模型: gpt-3.5-turbo
温度: 0.7
最大 tokens: 2000
=== 价格表 ===
gpt-3.5-turbo: $0.002/1K tokens
gpt-4: $0.03/1K tokens
claude-3: $0.015/1K tokens
=== 系统消息 ===
helper: 你是一个有帮助的AI助手
coder: 你是一个专业的编程助手
translator: 你是一个专业的翻译助手
今日总结
核心知识点回顾
1. 函数
- 使用
def定义函数 - 参数:位置参数、关键字参数、默认参数
- 返回值:使用
return,可以返回多个值(元组) - 函数的作用:代码复用、逻辑封装
2. 变量作用域
- 局部变量:函数内部定义
- 全局变量:函数外部定义
- 避免在函数内修改全局变量
3. lambda 表达式
- 语法:
lambda 参数: 表达式 - 适合简单的一次性函数
- 常用于排序、过滤、映射
4. 模块
- 标准库:直接
import使用 - 自定义模块:创建
.py文件 __name__ == "__main__"判断是否直接运行
5. 包管理
- pip:安装、卸载第三方包
- requirements.txt:记录项目依赖
- 虚拟环境:隔离项目依赖
函数设计原则
- 单一职责:一个函数只做一件事
- 参数适量:不要超过 5 个参数
- 命名清晰:函数名表达功能
- 避免副作用:尽量通过返回值而不是修改全局变量
- 添加文档:复杂函数写 docstring
常见问题
Q:什么时候应该把代码封装成函数? A:当一段代码需要重复使用,或者逻辑独立、可以单独命名时。
Q:参数传递是值传递还是引用传递? A:Python 中都是”传对象引用”。对于不可变类型(int、str、tuple),表现像值传递;对于可变类型(list、dict),修改会影响原对象。
Q:为什么要用虚拟环境? A:避免不同项目的依赖冲突。项目 A 用 requests 2.28,项目 B 用 requests 3.0,没有虚拟环境会冲突。
Q:import 和 from import 有什么区别?
A:import module 导入整个模块,使用时要加模块名;from module import func 只导入特定内容,使用时直接用。
Q:什么时候用 lambda,什么时候用普通函数? A:简单的一次性操作用 lambda(如排序的 key 函数),复杂逻辑或需要重用的用普通函数。
重点注意事项
-
默认参数的陷阱:不要用可变对象(list、dict)作为默认参数
# 错误 def add_item(item, lst=[]): # 默认参数是可变对象 lst.append(item) return lst # 正确 def add_item(item, lst=None): if lst is None: lst = [] lst.append(item) return lst -
模块导入顺序:标准库 → 第三方库 → 自定义模块
import os import json import requests from utils import count_tokens -
避免循环导入:A 导入 B,B 又导入 A,会报错
-
函数返回值:如果函数没有明确返回值,返回 None