Python 基础课:循环与核心数据结构
学习目标
今天结束后,你能够:
- 使用 for 和 while 循环处理重复任务
- 掌握 break 和 continue 控制循环流程
- 使用列表存储和管理批量数据
- 使用字典表示键值对关系
- 理解元组和集合的应用场景
- 用列表推导式简洁地创建列表
- 构建多轮对话消息管理系统
循环
循环让程序能够重复执行相同的代码块,是处理批量数据的基础。
for 循环
for 循环用于遍历序列(字符串、列表、字典等)中的每个元素。
基本语法:
for 变量 in 序列:
# 循环体代码
# 每次循环,变量会依次取序列中的一个值
遍历字符串
字符串也是序列,可以遍历其中的每个字符。
text = "Python"
for char in text:
print(char)
输出:
P
y
t
h
o
n
遍历列表
models = ["gpt-3.5-turbo", "gpt-4", "claude-3"]
for model in models:
print(f"模型: {model}")
输出:
模型: gpt-3.5-turbo
模型: gpt-4
模型: claude-3
使用 range() 生成数字序列
range() 是 Python 的内置函数,用于生成连续的整数序列。当需要循环指定次数时使用。
# range(n) 生成从 0 到 n-1 的整数
for i in range(5):
print(i)
输出:
0
1
2
3
4
range() 的三种用法:
range(5) # 生成 0, 1, 2, 3, 4
range(2, 5) # 生成 2, 3, 4(起始值2,结束值5,不包含5)
range(0, 10, 2) # 生成 0, 2, 4, 6, 8(起始0,结束10,步长2)
# 示例:打印 1 到 10
for i in range(1, 11):
print(i, end=" ") # end=" " 让输出用空格分隔而不是换行
# 输出:1 2 3 4 5 6 7 8 9 10
enumerate() 同时获取索引和值
enumerate() 是 Python 的内置函数,在遍历序列时同时返回元素的索引(位置)和值。当需要知道”这是第几个元素”时使用。
messages = ["你好", "天气不错", "再见"]
for index, msg in enumerate(messages):
print(f"{index}: {msg}")
输出:
0: 你好
1: 天气不错
2: 再见
enumerate() 可以指定起始索引(默认从 0 开始):
messages = ["你好", "天气不错", "再见"]
for index, msg in enumerate(messages, 1): # 从 1 开始计数
print(f"{index}. {msg}")
输出:
1. 你好
2. 天气不错
3. 再见
zip() 并行遍历多个序列
zip() 是 Python 的内置函数,可以同时遍历多个序列,将它们对应位置的元素配对在一起。
names = ["GPT-4", "Claude", "Gemini"]
prices = [0.03, 0.015, 0.002]
for name, price in zip(names, prices):
print(f"{name}: ${price}/1K tokens")
输出:
GPT-4: $0.03/1K tokens
Claude: $0.015/1K tokens
Gemini: $0.002/1K tokens
zip() 会以最短序列的长度为准:
list1 = [1, 2, 3]
list2 = ['a', 'b']
for x, y in zip(list1, list2):
print(x, y)
# 输出:
# 1 a
# 2 b
# (3 没有配对,被忽略)
while 循环
while 循环在条件为真时持续执行,适合不确定循环次数的场景。
count = 0
while count < 3:
print(f"第 {count + 1} 次循环")
count += 1
输出:
第 1 次循环
第 2 次循环
第 3 次循环
注意死循环
# 错误:忘记更新条件,导致死循环
count = 0
while count < 3:
print("循环中")
# 忘记 count += 1
这会无限循环,需要按 Ctrl+C 终止。
break 和 continue
break:跳出整个循环
for i in range(10):
if i == 5:
break
print(i)
输出:
0
1
2
3
4
continue:跳过本次循环,继续下一次
for i in range(5):
if i == 2:
continue
print(i)
输出:
0
1
3
4
AI 场景示例
# 模拟多轮对话
max_rounds = 3
round_count = 0
while round_count < max_rounds:
user_input = input(f"[第{round_count + 1}轮] 用户: ")
# lower() 是字符串方法,将字符串转为小写,方便不区分大小写的比较
if user_input.lower() == "exit":
print("退出对话")
break
# strip() 是字符串方法,去除首尾空格
if user_input.strip() == "":
print("输入为空,请重新输入")
continue
# 模拟 AI 回复
print(f"AI: 收到您的消息「{user_input}」")
round_count += 1
print(f"对话结束,共 {round_count} 轮")
课堂练习
批量 token 计算器
编写程序,输入多段文本(输入 “done” 结束),计算每段文本的预估 token 数(字符数 ÷ 4)。
参考答案:
print("=== 批量 Token 计算器 ===")
texts = []
while True:
text = input("请输入文本(输入done结束): ")
if text.lower() == "done":
break
texts.append(text)
print("\n计算结果:")
for i, text in enumerate(texts, 1): # enumerate(texts, 1) 让索引从1开始,而不是0
token_count = len(text) // 4
print(f"{i}. {text[:20]}... - {token_count} tokens")
# text[:20] 是字符串切片,取前20个字符,用于显示文本预览
列表(List)
列表是 Python 中最常用的数据结构,用于存储有序的元素集合。
列表创建
# 空列表
messages = []
# 初始化列表
models = ["gpt-3.5-turbo", "gpt-4", "claude-3"]
# 不同类型的元素(不推荐,但语法允许)
mixed = [1, "hello", True, 3.14]
列表索引和切片
models = ["gpt-3.5-turbo", "gpt-4", "claude-3", "gemini-pro"]
print(models[0]) # gpt-3.5-turbo(第一个)
print(models[-1]) # gemini-pro(最后一个)
print(models[1:3]) # ['gpt-4', 'claude-3'](切片)
print(models[:2]) # ['gpt-3.5-turbo', 'gpt-4'](前两个)
print(models[2:]) # ['claude-3', 'gemini-pro'](从第3个到末尾)
列表常用方法
messages = []
# 添加元素
messages.append("你好") # 在末尾添加
messages.append("天气不错")
print(messages) # ['你好', '天气不错']
# 插入元素
messages.insert(1, "早上好") # 在索引1处插入
print(messages) # ['你好', '早上好', '天气不错']
# 扩展列表
more_messages = ["再见", "晚安"]
messages.extend(more_messages)
print(messages) # ['你好', '早上好', '天气不错', '再见', '晚安']
# 删除元素
messages.remove("早上好") # 删除指定值
print(messages) # ['你好', '天气不错', '再见', '晚安']
last = messages.pop() # 删除并返回最后一个元素
print(last) # 晚安
print(messages) # ['你好', '天气不错', '再见']
# 获取长度
print(len(messages)) # 3
# len() 是内置函数,返回序列(列表、字符串、字典等)的长度(元素个数)
# 检查元素是否存在
print("你好" in messages) # True
print("早安" in messages) # False
# in 运算符检查元素是否在列表中,返回 True 或 False
# 排序
numbers = [3, 1, 4, 1, 5]
numbers.sort() # sort() 会直接修改原列表,按从小到大排序
print(numbers) # [1, 1, 3, 4, 5]
# 反转
numbers.reverse() # reverse() 会直接修改原列表,颠倒元素顺序
print(numbers) # [5, 4, 3, 1, 1]
列表推导式
列表推导式是创建列表的简洁方式,可以用一行代码完成”遍历→处理→生成新列表”的操作。
基本语法:
[表达式 for 变量 in 序列]
基础示例
# 传统方式:创建平方数列表
squares = []
for i in range(5):
squares.append(i ** 2)
print(squares) # [0, 1, 4, 9, 16]
# 列表推导式:一行搞定
squares = [i ** 2 for i in range(5)]
print(squares) # [0, 1, 4, 9, 16]
带条件过滤
语法:[表达式 for 变量 in 序列 if 条件]
# 只保留偶数
evens = [i for i in range(10) if i % 2 == 0]
print(evens) # [0, 2, 4, 6, 8]
# 转换大小写
words = ["Hello", "WORLD", "Python"]
lower_words = [w.lower() for w in words]
print(lower_words) # ['hello', 'world', 'python']
AI 场景示例
# 消息列表管理
messages = []
# 添加系统消息
messages.append({
"role": "system",
"content": "你是一个 Python 编程助手"
})
# 添加用户消息
messages.append({
"role": "user",
"content": "如何读取文件?"
})
# 添加助手消息
messages.append({
"role": "assistant",
"content": "可以使用 open() 函数..."
})
# 打印所有消息
for msg in messages:
print(f"[{msg['role']}] {msg['content']}")
输出:
[system] 你是一个 Python 编程助手
[user] 如何读取文件?
[assistant] 可以使用 open() 函数...
课堂练习
消息过滤器
有一个消息列表,编写程序筛选出所有用户消息。
messages = [
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!"},
{"role": "user", "content": "天气如何"},
]
参考答案:
# 方式1:循环
user_messages = []
for msg in messages:
if msg["role"] == "user":
user_messages.append(msg)
# 方式2:列表推导式(推荐)
user_messages = [msg for msg in messages if msg["role"] == "user"]
print(f"用户消息数: {len(user_messages)}")
for msg in user_messages:
print(f"- {msg['content']}")
输出:
用户消息数: 2
- 你好
- 天气如何
字典(Dictionary)
字典用于存储键值对,是 AI 开发中最重要的数据结构之一,与 JSON 数据高度对应。
字典创建
# 空字典
config = {}
# 初始化字典
model_config = {
"model": "gpt-4",
"temperature": 0.7,
"max_tokens": 2000
}
# 字典的键必须是不可变类型(字符串、数字、元组)
# 值可以是任何类型
字典访问与修改
config = {
"model": "gpt-4",
"temperature": 0.7,
"max_tokens": 2000
}
# 访问值
print(config["model"]) # gpt-4
print(config["temperature"]) # 0.7
# 修改值
config["temperature"] = 0.8
print(config["temperature"]) # 0.8
# 添加新键值对
config["stream"] = True
print(config)
# {'model': 'gpt-4', 'temperature': 0.8, 'max_tokens': 2000, 'stream': True}
# 删除键值对
del config["stream"]
安全访问:get() 方法
config = {"model": "gpt-4"}
# 直接访问不存在的键会报错
# print(config["temperature"]) # KeyError
# 使用 get() 安全访问
print(config.get("temperature")) # None
print(config.get("temperature", 0.7)) # 0.7(默认值)
字典常用方法
config = {
"model": "gpt-4",
"temperature": 0.7,
"max_tokens": 2000
}
# 获取所有键
print(config.keys()) # dict_keys(['model', 'temperature', 'max_tokens'])
# keys() 返回字典中所有的键
# 获取所有值
print(config.values()) # dict_values(['gpt-4', 0.7, 2000])
# values() 返回字典中所有的值
# 获取所有键值对
print(config.items()) # dict_items([('model', 'gpt-4'), ('temperature', 0.7), ...])
# items() 返回字典中所有的键值对,每个键值对是一个元组 (key, value)
# 遍历字典的键
for key in config:
print(f"{key}: {config[key]}")
# 遍历键值对(推荐方式)
for key, value in config.items():
print(f"{key}: {value}")
# 使用 items() 可以同时获取键和值,更方便
# 更新字典
new_config = {"stream": True, "temperature": 0.8}
config.update(new_config) # update() 将另一个字典的内容合并进来
print(config)
# 如果键已存在,会覆盖原值;如果不存在,会添加新键值对
# 检查键是否存在
print("model" in config) # True
print("api_key" in config) # False
# in 检查字典中是否存在某个键
字典嵌套
字典可以嵌套,用于表示复杂的结构化数据。
message = {
"role": "user",
"content": "如何使用 Python?",
"metadata": {
"timestamp": "2024-01-01 10:00:00",
"user_id": "user123"
}
}
print(message["role"]) # user
print(message["metadata"]["user_id"]) # user123
AI 场景示例
# 工具配置
tools = [
{
"name": "get_weather",
"description": "获取指定城市的天气",
"parameters": {
"city": {"type": "string", "required": True}
}
},
{
"name": "calculator",
"description": "执行数学计算",
"parameters": {
"expression": {"type": "string", "required": True}
}
}
]
# 遍历工具
for tool in tools:
print(f"工具: {tool['name']}")
print(f"描述: {tool['description']}")
print(f"参数: {tool['parameters']}")
print("---")
输出:
工具: get_weather
描述: 获取指定城市的天气
参数: {'city': {'type': 'string', 'required': True}}
---
工具: calculator
描述: 执行数学计算
参数: {'expression': {'type': 'string', 'required': True}}
---
课堂练习
配置合并器
编写程序,将用户配置和默认配置合并,用户配置优先级更高。
default_config = {
"model": "gpt-3.5-turbo",
"temperature": 0.7,
"max_tokens": 1000,
"stream": False
}
user_config = {
"model": "gpt-4",
"temperature": 0.9
}
参考答案:
default_config = {
"model": "gpt-3.5-turbo",
"temperature": 0.7,
"max_tokens": 1000,
"stream": False
}
user_config = {
"model": "gpt-4",
"temperature": 0.9
}
# 合并配置
final_config = default_config.copy() # copy() 创建字典的副本,避免修改原字典
final_config.update(user_config)
print("最终配置:")
for key, value in final_config.items():
print(f" {key}: {value}")
输出:
最终配置:
model: gpt-4
temperature: 0.9
max_tokens: 1000
stream: False
元组(Tuple)
元组是不可变的序列,一旦创建就不能修改。
元组创建
# 空元组
empty = ()
# 包含元素的元组
point = (10, 20)
rgb = (255, 128, 0)
# 单元素元组(注意逗号)
single = (5,) # 正确
not_tuple = (5) # 这是整数,不是元组
元组操作
point = (10, 20, 30)
# 索引访问
print(point[0]) # 10
print(point[-1]) # 30
# 切片
print(point[1:]) # (20, 30)
# 不能修改
# point[0] = 15 # TypeError: 'tuple' object does not support item assignment
# 元组解包
x, y, z = point
print(f"x={x}, y={y}, z={z}") # x=10, y=20, z=30
# 遍历
for value in point:
print(value)
元组的应用场景
函数多返回值
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}")
作为字典的键
# 列表不能作为字典的键,元组可以
coordinates = {
(0, 0): "原点",
(1, 0): "右侧",
(0, 1): "上方"
}
print(coordinates[(0, 0)]) # 原点
元组 vs 列表
| 特性 | 列表 | 元组 |
|---|---|---|
| 可变性 | 可变 | 不可变 |
| 语法 | [1, 2, 3] | (1, 2, 3) |
| 性能 | 稍慢 | 稍快 |
| 应用 | 同类数据集合 | 固定结构数据 |
集合(Set)
集合是无序、不重复的元素集合,主要用于去重和集合运算。
集合创建
# 使用 {} 创建非空集合
tags = {"Python", "AI", "GPT"}
# 使用 set() 从列表创建集合(自动去重)
numbers = set([1, 2, 2, 3, 3, 3])
print(numbers) # {1, 2, 3}
# 空集合必须用 set()
empty = set() # 正确:创建空集合
# empty = {} # 错误:这是空字典,不是空集合
# 因为 {} 优先表示字典,所以空集合要用 set()
集合操作
tags = {"Python", "AI"}
# 添加元素
tags.add("GPT")
print(tags) # {'Python', 'AI', 'GPT'}
# 删除元素
tags.remove("AI")
print(tags) # {'Python', 'GPT'}
# 检查是否存在
print("Python" in tags) # True
# 长度
print(len(tags)) # 2
集合运算
skills_a = {"Python", "AI", "SQL"}
skills_b = {"Python", "Java", "AI"}
# 交集(共同拥有)
print(skills_a & skills_b) # {'Python', 'AI'}
print(skills_a.intersection(skills_b)) # 等价写法
# 并集(合并)
print(skills_a | skills_b) # {'Python', 'AI', 'SQL', 'Java'}
print(skills_a.union(skills_b)) # 等价写法
# 差集(A有但B没有)
print(skills_a - skills_b) # {'SQL'}
print(skills_a.difference(skills_b)) # 等价写法
# 对称差集(A或B独有)
print(skills_a ^ skills_b) # {'SQL', 'Java'}
AI 场景示例
# 文档去重
docs = [
"Python入门.txt",
"AI基础.txt",
"Python入门.txt", # 重复
"API文档.txt",
"AI基础.txt" # 重复
]
# 去重
unique_docs = set(docs)
print(f"原始: {len(docs)} 个文档")
print(f"去重后: {len(unique_docs)} 个文档")
print(unique_docs)
输出:
原始: 5 个文档
去重后: 3 个文档
{'AI基础.txt', 'Python入门.txt', 'API文档.txt'}
课堂练习
关键词统计
给定多个用户问题,统计所有出现过的关键词(去重)。
参考答案:
questions = [
"Python 如何读取文件",
"如何使用 Python 处理 JSON",
"Python 异常处理",
"文件读取错误怎么办"
]
keywords = set()
for question in questions:
words = question.split()
for word in words:
keywords.add(word)
print(f"关键词总数: {len(keywords)}")
print(sorted(keywords)) # sorted() 返回排序后的列表,不修改原集合
输出:
关键词总数: 11
['JSON', 'Python', 'processing', '如何', '处理', '异常', '文件', '怎么办', '读取', '错误', '使用']
序列通用操作
列表、元组、字符串都是序列类型,支持一些通用操作。
拼接和重复
# 拼接
list1 = [1, 2]
list2 = [3, 4]
print(list1 + list2) # [1, 2, 3, 4]
# 重复
print([0] * 5) # [0, 0, 0, 0, 0]
print("=" * 20) # ====================
成员判断
messages = ["你好", "天气不错", "再见"]
print("你好" in messages) # True
print("晚安" in messages) # False
print("晚安" not in messages) # True
长度、最大值、最小值
Python 提供了一些内置函数,可以对序列进行统计操作。
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(len(numbers)) # 8 - 返回元素个数
print(max(numbers)) # 9 - 返回最大值
print(min(numbers)) # 1 - 返回最小值
print(sum(numbers)) # 31 - 返回所有元素的总和(仅限数字)
列表、元组、集合的选择原则
| 场景 | 推荐类型 |
|---|---|
| 需要修改(增删改) | 列表 |
| 不需要修改,性能优先 | 元组 |
| 需要去重 | 集合 |
| 需要顺序 | 列表或元组 |
| 需要键值对 | 字典 |
当天作业
多轮对话消息管理器
编写一个命令行程序,实现以下功能:
- 用户可以输入多轮消息
- 每条消息保存为字典格式
{"role": "user", "content": "..."} - 输入 “history” 查看所有历史消息
- 输入 “clear” 清空历史
- 输入 “exit” 退出程序
参考答案:
print("=== 多轮对话消息管理器 ===")
print("命令: history(查看历史), clear(清空), exit(退出)\n")
messages = []
while True:
user_input = input("用户: ")
if user_input == "exit":
print("退出程序")
break
if user_input == "history":
if not messages:
print("暂无历史消息")
else:
print(f"\n共 {len(messages)} 条消息:")
for i, msg in enumerate(messages, 1):
print(f"{i}. [{msg['role']}] {msg['content']}")
print()
continue
if user_input == "clear":
messages = []
print("历史已清空\n")
continue
if user_input.strip() == "":
print("输入为空,请重新输入\n")
continue
# 保存用户消息
messages.append({
"role": "user",
"content": user_input
})
# 模拟 AI 回复
ai_response = f"收到您的消息: {user_input}"
messages.append({
"role": "assistant",
"content": ai_response
})
print(f"AI: {ai_response}\n")
Prompt 模板管理器
创建一个 prompt 模板字典,根据用户选择的任务类型返回对应的模板。
要求:
- 至少包含 3 种任务类型(代码编写、文本分析、翻译)
- 每个模板是一个字典,包含 system_prompt 和 user_template
- 用户选择任务类型后,输入具体内容,生成完整 prompt
参考答案:
templates = {
"code": {
"system_prompt": "你是一个专业的编程助手,擅长编写高质量代码。",
"user_template": "请帮我编写: {task}"
},
"analyze": {
"system_prompt": "你是一个文本分析专家,擅长理解和分析文本。",
"user_template": "请分析以下内容: {task}"
},
"translate": {
"system_prompt": "你是一个专业翻译,精通中英互译。",
"user_template": "请翻译: {task}"
}
}
print("=== Prompt 模板管理器 ===")
print("任务类型:")
for key, value in templates.items():
print(f" {key}: {value['system_prompt']}")
task_type = input("\n选择任务类型: ")
if task_type in templates:
user_input = input("输入任务内容: ")
template = templates[task_type]
system = template["system_prompt"]
user = template["user_template"].format(task=user_input)
# format() 是字符串方法,用于替换字符串中的 {task} 占位符
print("\n" + "="*60)
print("[System]", system)
print("[User]", user)
print("="*60)
else:
print("无效的任务类型")
工具配置去重
给定一个包含重复工具的列表,根据工具名称去重。
tools = [
{"name": "calculator", "desc": "计算器"},
{"name": "weather", "desc": "天气查询"},
{"name": "calculator", "desc": "计算器"}, # 重复
{"name": "search", "desc": "搜索"},
{"name": "weather", "desc": "天气查询"} # 重复
]
参考答案:
tools = [
{"name": "calculator", "desc": "计算器"},
{"name": "weather", "desc": "天气查询"},
{"name": "calculator", "desc": "计算器"},
{"name": "search", "desc": "搜索"},
{"name": "weather", "desc": "天气查询"}
]
# 使用集合记录已见过的名称
seen_names = set()
unique_tools = []
for tool in tools:
if tool["name"] not in seen_names:
seen_names.add(tool["name"])
unique_tools.append(tool)
print(f"原始工具数: {len(tools)}")
print(f"去重后: {len(unique_tools)}")
print("\n去重后的工具:")
for tool in unique_tools:
print(f" - {tool['name']}: {tool['desc']}")
输出:
原始工具数: 5
去重后: 3
去重后的工具:
- calculator: 计算器
- weather: 天气查询
- search: 搜索
消息统计分析器
编写程序,分析消息列表,统计:
- 用户消息数量
- 助手消息数量
- 平均消息长度
- 最长的消息
messages = [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!有什么可以帮你的吗?"},
{"role": "user", "content": "如何学习 Python"},
{"role": "assistant", "content": "学习 Python 可以从基础语法开始..."},
{"role": "user", "content": "谢谢"}
]
参考答案:
messages = [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!有什么可以帮你的吗?"},
{"role": "user", "content": "如何学习 Python"},
{"role": "assistant", "content": "学习 Python 可以从基础语法开始..."},
{"role": "user", "content": "谢谢"}
]
user_count = 0
assistant_count = 0
total_length = 0
longest_msg = ""
for msg in messages:
content = msg["content"]
if msg["role"] == "user":
user_count += 1
elif msg["role"] == "assistant":
assistant_count += 1
total_length += len(content)
if len(content) > len(longest_msg):
longest_msg = content
avg_length = total_length / len(messages) if messages else 0
print("=== 消息统计 ===")
print(f"用户消息: {user_count} 条")
print(f"助手消息: {assistant_count} 条")
print(f"平均长度: {avg_length:.1f} 字符")
print(f"最长消息: {longest_msg}")
输出:
=== 消息统计 ===
用户消息: 3 条
助手消息: 2 条
平均长度: 10.0 字符
最长消息: 学习 Python 可以从基础语法开始...
浅拷贝与深拷贝
在处理列表和字典时,理解拷贝机制很重要,尤其是处理嵌套结构(列表套字典、字典套列表)时。
为什么需要拷贝
# 直接赋值:不是拷贝,只是引用
messages = [{"role": "user", "content": "你好"}]
messages2 = messages # 只是创建了一个新的引用
messages2.append({"role": "assistant", "content": "你好!"})
print(len(messages)) # 2(原列表也被修改了!)
print(len(messages2)) # 2
直接赋值不会创建新对象,两个变量指向同一个列表。
浅拷贝(Shallow Copy)
浅拷贝创建新的外层对象,但内层对象仍然共享。
三种方式:
# 方式1:切片
list1 = [1, 2, 3]
list2 = list1[:]
# 方式2:copy()方法
list3 = list1.copy()
# 方式3:copy模块
import copy
list4 = copy.copy(list1)
简单数据结构的浅拷贝:
# 简单列表(元素是不可变类型)
models = ["gpt-3.5", "gpt-4", "claude"]
models_copy = models.copy()
models_copy.append("gemini")
print(models) # ['gpt-3.5', 'gpt-4', 'claude'](不受影响)
print(models_copy) # ['gpt-3.5', 'gpt-4', 'claude', 'gemini']
嵌套结构的浅拷贝问题:
# 嵌套列表(列表中包含字典)
messages = [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!"}
]
# 浅拷贝
messages_copy = messages.copy()
# 修改外层:互不影响
messages_copy.append({"role": "user", "content": "再见"})
print(len(messages)) # 2(不受影响)
print(len(messages_copy)) # 3
# 修改内层:会相互影响!
messages_copy[0]["content"] = "Hello"
print(messages[0]["content"]) # Hello(被影响了!)
print(messages_copy[0]["content"]) # Hello
深拷贝(Deep Copy)
深拷贝会递归复制所有层级的对象,完全独立。
import copy
messages = [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!"}
]
# 深拷贝
messages_deep = copy.deepcopy(messages)
# 修改内层:互不影响
messages_deep[0]["content"] = "Hello"
print(messages[0]["content"]) # 你好(不受影响)
print(messages_deep[0]["content"]) # Hello
AI 场景示例
import copy
# 原始配置
default_config = {
"model": "gpt-3.5-turbo",
"temperature": 0.7,
"params": {
"max_tokens": 2000,
"top_p": 1.0
}
}
# 浅拷贝:修改嵌套字典会影响原配置
config1 = default_config.copy()
config1["params"]["max_tokens"] = 1000
print(default_config["params"]["max_tokens"]) # 1000(被影响)
# 深拷贝:完全独立
config2 = copy.deepcopy(default_config)
config2["model"] = "gpt-4"
config2["params"]["max_tokens"] = 4000
print(default_config["model"]) # gpt-3.5-turbo(不受影响)
print(default_config["params"]["max_tokens"]) # 1000(不受影响)
实用建议
什么时候用浅拷贝:
- 列表只包含简单类型(数字、字符串)
- 不需要修改嵌套对象
什么时候用深拷贝:
- 列表或字典包含嵌套结构
- 需要完全独立的副本
- AI 开发中复制消息列表、配置对象
记忆口诀:
- 直接赋值 = 共享同一个对象
- 浅拷贝 = 新容器,旧内容
- 深拷贝 = 全新对象
课堂练习
消息列表的安全复制
有一个消息列表,需要创建多个独立的会话副本。
import copy
# 原始消息模板
template_messages = [
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"}
]
# 错误方式:直接赋值
session1 = template_messages
session1.append({"role": "assistant", "content": "你好!"})
print(len(template_messages)) # 3(模板被污染)
# 正确方式:深拷贝
template_messages = [
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"}
]
session2 = copy.deepcopy(template_messages)
session3 = copy.deepcopy(template_messages)
session2.append({"role": "assistant", "content": "你好!"})
session3.append({"role": "assistant", "content": "Hi!"})
print(len(template_messages)) # 2(模板不受影响)
print(len(session2)) # 3
print(len(session3)) # 3
今日总结
核心知识点回顾
1. 循环
- for 循环:遍历序列(字符串、列表、字典)
- while 循环:条件不确定时使用
- range():生成数字序列,支持起始、结束、步长
- enumerate():同时获取索引和值
- zip():并行遍历多个序列
- break:跳出循环
- continue:跳过本次循环
2. 列表(list)
- 有序、可变、可重复
- 常用方法:append()、insert()、extend()、remove()、pop()、sort()
- 索引和切片:[0]、[-1]、[1:3]
- 列表推导式:
[expr for item in iterable if condition]
3. 字典(dict)
- 键值对存储,键必须不可变
- 常用方法:get()、keys()、values()、items()、update()
- 字典嵌套:表示复杂数据结构
- 与 JSON 数据高度对应
4. 元组(tuple)
- 有序、不可变
- 用于固定结构数据、函数多返回值
- 可作为字典的键
5. 集合(set)
- 无序、不重复
- 用于去重
- 集合运算:交集(&)、并集(|)、差集(-)
6. 序列通用操作
- 拼接:+
- 重复:*
- 成员判断:in、not in
- 长度、最大、最小:len()、max()、min()、sum()
数据结构选择指南
| 需求 | 推荐 | 原因 |
|---|---|---|
| 需要修改(增删改) | 列表 | 可变 |
| 不需要修改 | 元组 | 不可变,性能更好 |
| 需要去重 | 集合 | 自动去重 |
| 需要键值对 | 字典 | key-value 映射 |
| 需要保持顺序 | 列表或元组 | 有序 |
| AI 消息列表 | 列表 + 字典 | [{"role": "...", "content": "..."}] |
| 工具配置 | 字典 | JSON 对应 |
| 文档去重 | 集合 | 自动去重 |
AI 开发中的应用
消息管理
messages = [
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!"}
]
工具配置
tools = {
"calculator": {"desc": "计算器", "params": ["expression"]},
"weather": {"desc": "天气", "params": ["city"]}
}
批量处理
# 批量处理文档
for doc in documents:
chunks = split_text(doc)
for chunk in chunks:
process(chunk)
常见问题
Q:列表和元组有什么区别? A:列表可变,元组不可变。列表用 [],元组用 ()。元组性能更好,适合不需要修改的数据。
Q:什么时候用列表推导式? A:需要从一个列表创建另一个列表时。比传统循环更简洁,但不要过度复杂化。
Q:字典的键可以是列表吗? A:不可以,键必须是不可变类型(字符串、数字、元组)。列表是可变的,不能作为键。
Q:如何选择用字典还是列表? A:如果数据有明确的 key-value 关系,用字典。如果只是一组有序的值,用列表。
Q:为什么 AI 开发中字典这么重要? A:因为 JSON 是 AI API 的标准数据格式,Python 字典与 JSON 高度对应,可以直接互转。
Q:集合什么时候用? A:主要用于去重和集合运算(交集、并集等)。比如去重文档名、关键词统计等。
重点注意事项
- 循环中的索引:Python 索引从 0 开始,不是 1
- range() 的边界:range(5) 生成 0-4,不包含 5
- 字典访问:访问不存在的键会报错,用 get() 更安全
- 列表是可变的:修改列表会影响所有引用
- 集合是无序的:不能用索引访问,不保证顺序
- 列表推导式不要过度复杂:超过两层嵌套就用传统循环