Files
PythonLearn/01_python基础/1_9_元组

第 1-9 课:元组

一、本课目标

完成本课后,你应该能够:

  1. 解释元组与列表的主要区别;
  2. 创建空元组、单元素元组和多元素元组;
  3. 使用索引、负索引和切片读取元组;
  4. 使用 len()inindex()count()
  5. 使用 for 遍历元组;
  6. 理解元组不可变的含义;
  7. 使用元组打包和解包多个数据;
  8. 使用星号收集解包后的剩余元素;
  9. 完成两个变量的交换;
  10. 在列表和元组之间进行转换;
  11. 判断什么场景更适合使用元组。

二、前置知识

开始本课前,你应该已经掌握:

  • 列表的创建、索引和切片;
  • 列表的可变性;
  • 使用 for 遍历多个数据;
  • 使用 inindex()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

练习会要求你创建配置元组、读取和切片、查找统计、解包、转换以及比较原配置和新配置。

二十二、思考题

  1. 列表和元组最重要的区别是什么?
  2. 为什么单元素元组必须保留逗号?
  3. 元组切片会返回列表还是元组?
  4. 解包时变量数量不匹配会发生什么?
  5. 星号变量收集剩余元素后得到什么类型?
  6. 为什么经常变化的数据更适合使用列表?
  7. 元组中包含列表时,为什么列表内容仍然能够变化?

二十三、本课小结

  • 元组是有顺序、不可变的数据集合;
  • 单元素元组必须包含逗号;
  • 元组支持索引、切片、遍历、成员判断和统计;
  • 元组不能直接增删或替换元素;
  • 需要新数据时,可以创建新元组或进行列表转换;
  • 打包把多个数据组合成元组;
  • 解包把元组元素分别赋给变量;
  • 星号可以收集剩余元素;
  • Python 可以通过打包解包交换变量;
  • 固定结构适合元组,经常变化的数据适合列表。

二十四、验收标准

  • 能创建空元组、单元素元组和多元素元组;
  • 能使用索引和切片读取元组;
  • 能使用 len()inindex()count()
  • 能遍历元组;
  • 能解释元组不可变;
  • 能完成打包和解包;
  • 能使用星号收集剩余元素;
  • 能在列表和元组之间转换;
  • 能完成并运行 practice.py
  • 能回答七道思考题。