第 1-9 课:元组
一、本课目标
完成本课后,你应该能够:
- 解释元组与列表的主要区别;
- 创建空元组、单元素元组和多元素元组;
- 使用索引、负索引和切片读取元组;
- 使用
len()、in、index()和count(); - 使用
for遍历元组; - 理解元组不可变的含义;
- 使用元组打包和解包多个数据;
- 使用星号收集解包后的剩余元素;
- 完成两个变量的交换;
- 在列表和元组之间进行转换;
- 判断什么场景更适合使用元组。
二、前置知识
开始本课前,你应该已经掌握:
- 列表的创建、索引和切片;
- 列表的可变性;
- 使用
for遍历多个数据; - 使用
in、index()和count(); - 条件判断与 f-string。
三、元组是什么
元组(Tuple)是一个有顺序的数据集合。
agent_config = ("gpt-5", 0.7, 2000)
元组与列表很相似:
- 都能保存多个数据;
- 都有顺序;
- 都支持索引、负索引和切片;
- 都能使用
len()和for。
最重要的区别是:
- 列表是可变的,可以添加、删除和修改元素;
- 元组是不可变的,创建后不能替换、增加或删除元素。
元组适合表达“创建后不希望被随意改变的一组数据”。
四、创建元组
4.1 多元素元组
agent_config = ("gpt-5", 0.7, 2000)
通常使用英文圆括号 () 包围元素,元素之间使用英文逗号分隔。
4.2 空元组
empty_tuple = ()
使用 len(empty_tuple) 会得到 0。
4.3 单元素元组
单元素元组必须在元素后保留英文逗号:
single_tool = ("Python",)
如果没有逗号:
not_a_tuple = ("Python")
Python 会把圆括号理解为普通分组,变量实际保存的是字符串。
可以使用 type() 验证:
print(type(("Python",))) # tuple
print(type(("Python"))) # str
创建元组时,真正决定元组的是逗号,而不只是圆括号。
4.4 省略圆括号
agent_position = "北京", "朝阳区"
只要存在用于分隔元素的逗号,Python 也会创建元组。为了新手阅读清晰,本课大多数场景仍保留圆括号。
五、元组可以保存不同类型
agent_config = ("gpt-5", 0.7, 2000, True)
这个元组依次保存:
- 字符串:模型名称;
- 浮点数:温度参数;
- 整数:最大输出长度;
- 布尔值:是否启用。
虽然允许混合类型,但每个位置应具有明确、稳定的含义。
六、索引和负索引
元组索引从 0 开始:
元素: gpt-5 0.7 2000
正索引: 0 1 2
负索引: -3 -2 -1
agent_config = ("gpt-5", 0.7, 2000)
print(agent_config[0])
print(agent_config[-1])
访问不存在的索引会产生 IndexError。
访问首尾元素前,可以先判断元组是否非空:
if agent_config:
print(agent_config[0])
print(agent_config[-1])
七、元组切片
切片规则与字符串、列表相同:包含开始索引,不包含结束索引。
agent_config = ("gpt-5", 0.7, 2000, True)
basic_config = agent_config[:2]
remaining_config = agent_config[2:]
reversed_config = agent_config[::-1]
元组切片返回一个新元组,不会修改原元组。
八、长度、成员判断和统计
8.1 len()
config_count = len(agent_config)
8.2 in
contains_model = "gpt-5" in agent_config
8.3 index()
if "gpt-5" in agent_config:
model_index = agent_config.index("gpt-5")
元素不存在时,index() 会产生 ValueError,因此可以先使用 in 判断。
8.4 count()
status_history = ("成功", "失败", "成功")
success_count = status_history.count("成功")
九、遍历元组
supported_models = ("gpt-5", "o3", "gpt-4.1")
for model in supported_models:
print(model)
需要编号时可以使用 enumerate():
for number, model in enumerate(supported_models, start=1):
print(f"{number}. {model}")
十、元组不可变
下面的操作都不允许:
agent_config[0] = "o3"
agent_config.append("新数据")
del agent_config[0]
元组没有 append()、remove()、pop() 等修改方法。
不可变带来的好处包括:
- 表达数据结构是固定的;
- 减少意外修改;
- 阅读代码时更容易判断数据不会被增删;
- 某些场景中元组可以作为字典的键,后续学习字典时会再讲。
十一、需要“修改”元组怎么办
不能直接修改原元组,但可以创建新元组:
old_config = ("gpt-5", 0.7, 2000)
new_config = ("o3",) + old_config[1:]
print(old_config)
print(new_config)
原元组保持不变,new_config 是全新的元组。
也可以先转换为列表,修改后再转回元组:
config_list = list(old_config)
config_list[0] = "o3"
new_config = tuple(config_list)
如果数据经常需要修改,直接使用列表通常更合适,不应反复转换。
十二、元组拼接和重复
虽然不能修改原元组,但可以通过运算创建新元组。
12.1 拼接
basic_config = ("gpt-5", 0.7)
limit_config = (2000, True)
full_config = basic_config + limit_config
12.2 重复
default_status = ("待处理",) * 3
结果为:
('待处理', '待处理', '待处理')
这两种操作都返回新元组。
十三、元组打包
把多个数据放进一个元组,称为打包(Packing):
agent_snapshot = "gpt-5", 0.7, 2000
三个数据被打包成一个元组。
使用圆括号可以让含义更清楚:
agent_snapshot = ("gpt-5", 0.7, 2000)
十四、元组解包
把元组中的元素分别赋给多个变量,称为解包(Unpacking):
agent_snapshot = ("gpt-5", 0.7, 2000)
model_name, temperature, max_tokens = agent_snapshot
解包后:
model_name保存"gpt-5";temperature保存0.7;max_tokens保存2000。
左侧变量数量必须与右侧元素数量一致,否则会产生 ValueError。
十五、使用星号收集剩余元素
agent_snapshot = ("gpt-5", 0.7, 2000, True)
model_name, *other_settings = agent_snapshot
解包后:
model_name是字符串"gpt-5";other_settings是列表[0.7, 2000, True]。
星号变量会把剩余元素收集到列表中。
一个解包表达式中只能有一个星号变量:
first_value, *middle_values, last_value = agent_snapshot
十六、交换变量
传统思路通常需要临时变量。Python 可以使用打包和解包直接交换:
primary_model = "gpt-5"
backup_model = "o3"
primary_model, backup_model = backup_model, primary_model
右侧先打包为元组,再解包给左侧变量。
十七、元组内部包含可变对象
请观察:
agent_data = ("gpt-5", ["搜索", "计算"])
agent_data[1].append("写作")
元组本身仍然不可变:不能把 agent_data[1] 替换成另一个列表。
但是第二个元素指向的列表本身是可变对象,因此列表内部可以改变。
这说明元组不可变的准确含义是:元组保存的元素引用不能被替换,并不保证元素内部的数据一定不可变。
这是一个重要边界。本课能看懂即可,后续学习对象和引用时会继续加深理解。
十八、列表与元组如何选择
适合使用列表:
- 数据需要经常添加、删除、修改;
- 数量会变化;
- 表达任务清单、工具集合、学习记录。
适合使用元组:
- 数据结构固定;
- 创建后不希望改变;
- 表达坐标、颜色值、固定配置、数据库返回的一条固定结构记录;
- 需要通过解包一次取得多个数据。
不要因为元组“看起来更高级”而强行使用。应该根据数据是否需要变化来选择。
十九、运行本课示例
在 PowerShell 中执行:
cd D:\Code\Python\01_python基础\1_9_元组
python tuples.py
示例无需用户输入,会依次展示创建、读取、切片、统计、遍历、拼接、转换、打包、解包和变量交换。
二十、常见错误
20.1 单元素元组忘记逗号
single_value = ("Python",)
最后的逗号不可省略。
20.2 尝试直接修改元组
元组不支持按索引赋值,也没有列表的增删方法。
20.3 解包变量数量不匹配
三个元素通常需要三个接收变量,除非使用星号收集剩余元素。
20.4 认为圆括号一定代表元组
("Python") 是字符串;逗号才是创建单元素元组的关键。
20.5 把元组中的列表也当作完全不可变
元组不能替换元素,但元组内部的列表仍然可以修改自身内容。
二十一、课堂练习
打开 practice.py,完成“Agent 固定配置快照”。
运行命令:
python practice.py
练习会要求你创建配置元组、读取和切片、查找统计、解包、转换以及比较原配置和新配置。
二十二、思考题
- 列表和元组最重要的区别是什么?
- 为什么单元素元组必须保留逗号?
- 元组切片会返回列表还是元组?
- 解包时变量数量不匹配会发生什么?
- 星号变量收集剩余元素后得到什么类型?
- 为什么经常变化的数据更适合使用列表?
- 元组中包含列表时,为什么列表内容仍然能够变化?
二十三、本课小结
- 元组是有顺序、不可变的数据集合;
- 单元素元组必须包含逗号;
- 元组支持索引、切片、遍历、成员判断和统计;
- 元组不能直接增删或替换元素;
- 需要新数据时,可以创建新元组或进行列表转换;
- 打包把多个数据组合成元组;
- 解包把元组元素分别赋给变量;
- 星号可以收集剩余元素;
- Python 可以通过打包解包交换变量;
- 固定结构适合元组,经常变化的数据适合列表。
二十四、验收标准
- 能创建空元组、单元素元组和多元素元组;
- 能使用索引和切片读取元组;
- 能使用
len()、in、index()和count(); - 能遍历元组;
- 能解释元组不可变;
- 能完成打包和解包;
- 能使用星号收集剩余元素;
- 能在列表和元组之间转换;
- 能完成并运行
practice.py; - 能回答七道思考题。