1. 元组
元组(tuple)也是一种有序集合,可以保存多个值。它和列表很像,都支持索引、切片和遍历,但元组创建后不能直接修改其中的元素。
使用圆括号创建元组:
1point = (10, 20)2topics = ("变量", "列表", "元组")3empty = ()45print(point)6print(topics)7print(empty)
圆括号在很多场景中可以省略,逗号才是元组结构的重要标记:
1point = 10, 2023print(point) # (10, 20)4print(type(point)) # <class 'tuple'>
创建只有一个元素的元组时,必须保留逗号。没有逗号的 ("Python") 只是普通字符串加上括号:
1single_tuple = ("Python",)2not_tuple = ("Python")34print(type(single_tuple)) # <class 'tuple'>5print(type(not_tuple)) # <class 'str'>
也可以使用 tuple() 把其他可遍历对象转换成元组:
1letters = tuple("Python")2numbers = tuple(range(3))34print(letters) # ('P', 'y', 't', 'h', 'o', 'n')5print(numbers) # (0, 1, 2)
2. 访问
元组的索引和切片规则与列表、字符串一致,索引从 0 开始:
1topics = ("变量", "列表", "元组", "字符串")23print(topics[0]) # 变量4print(topics[-1]) # 字符串5print(topics[1:3]) # ('列表', '元组')
可以使用 len() 获取元组长度,使用 in 判断元素是否存在:
1permissions = ("read", "write")23print(len(permissions)) # 24print("read" in permissions) # True5print("admin" not in permissions) # True
元组支持 count() 和 index():
1values = ("Python", "AI", "Python")23print(values.count("Python")) # 24print(values.index("AI")) # 1
如果 index() 找不到目标元素,会抛出 ValueError。不确定元素是否存在时,先使用 in 判断。
3. 不可变
元组最重要的特征是不可变。创建之后,不能给某个位置重新赋值,也不能使用 append()、remove() 等方法修改结构:
1topics = ("变量", "列表", "元组")23# topics[0] = "模块" # TypeError4# topics.append("字符串") # AttributeError
如果确实需要得到一个加入新元素的结果,应该创建一个新的元组:
1topics = ("变量", "列表")2new_topics = topics + ("元组",)34print(topics) # ('变量', '列表')5print(new_topics) # ('变量', '列表', '元组')
这里的 + 不会修改原元组,而是返回一个新的元组。单元素元组中的逗号不能省略。
元组不可变,但如果元组里面保存的是列表,列表本身仍然可以修改:
1data = ("topics", ["变量", "列表"])2data[1].append("元组")34print(data) # ('topics', ['变量', '列表', '元组'])
不可变的是元组保存的元素引用,不能通过元组索引替换第二个元素;内部列表作为独立对象,仍然保留自己的可变性。
4. 解包
元组经常用于一次性保存一组固定值,也经常用于解包:把元组中的元素依次赋给多个变量:
1point = (10, 20)2x, y = point34print(x) # 105print(y) # 20
左侧变量的数量必须和右侧元素数量匹配,否则会抛出 ValueError:
1values = (1, 2, 3)23# first, second = values # ValueError
如果只关心一部分元素,可以使用 * 收集剩余内容:
1values = (1, 2, 3, 4, 5)2first, *middle, last = values34print(first) # 15print(middle) # [2, 3, 4]6print(last) # 5
带 * 的变量得到的是列表,而不是元组。一个解包表达式中通常只使用一个带 * 的变量。
函数可以返回多个值,Python 实际上会把它们组合成元组:
1def get_user():2return "小明", 1834name, age = get_user()5print(name)6print(age)
这种写法比手动创建一个临时对象更简洁,适合返回几个关系紧密的结果。
5. 对比
列表和元组都可以保存有序数据,但用途有所不同:
| 对比项 | 列表 | 元组 |
|---|---|---|
| 创建符号 | [] | () |
| 是否有序 | 是 | 是 |
| 是否可修改 | 可以 | 不可以 |
| 常用场景 | 数据需要增删改 | 数据结构固定 |
| 是否支持索引切片 | 支持 | 支持 |
| 是否可作为字典键 | 通常不可以 | 元素都可哈希时可以 |
需要不断添加任务、删除结果或重新排序时,使用列表更合适:
1tasks = ["读取文件", "保存结果"]2tasks.append("发送通知")34print(tasks)
表示坐标、配置项、日期或函数返回的固定组合时,元组更能表达「这些位置和数量不会随意变化」的意图:
1rgb = (255, 128, 0)2version = (3, 12, 1)34print(rgb)5print(version)
元组中的元素都可哈希时,可以作为字典的键。列表因为可变,不能直接作为字典键:
1locations = {2(31.23, 121.47): "上海",3(39.90, 116.40): "北京",4}56print(locations[(31.23, 121.47)]) # 上海
6. 使用
元组可以直接遍历,也可以和 enumerate() 一起使用:
1topics = ("变量", "列表", "元组")23for index, topic in enumerate(topics, start=1):4print(f"第 {index} 个主题:{topic}")
在处理固定结构的数据时,可以把元组解包后再使用:
1records = (2("小明", 82),3("小红", 95),4("小李", 58),5)67for name, score in records:8if score >= 60:9print(f"{name}:通过")
下面的例子使用元组保存固定的状态值,再用字典把状态值映射为展示文本:
01statuses = ("pending", "running", "success", "failed")02labels = {03"pending": "等待中",04"running": "执行中",05"success": "执行成功",06"failed": "执行失败",07}0809for status in statuses:10print(labels[status])
FastAPI 接口和 LangChain 工具中也经常返回固定结构的多个结果。例如把状态和消息一起返回,再由调用方解包:
1def run_task():2return True, "任务执行成功"34success, message = run_task()5if success:6print(message)
当返回值的数量和含义稳定时,元组可以让调用方一眼看出数据结构;如果字段很多、需要按名称访问,则更适合使用字典或数据模型。