Skip to content

Python 元组 ​

上一节学习的列表可以随时增加、删除和修改元素。现在有一个新需求:保存地图坐标、日期信息或固定配置,并避免数据在后续操作中被意外改动,应该选择哪种容器?

元组适合保存这类数据。它和列表一样有顺序,也支持索引和切片;区别在于,元组创建后不能直接修改。

案例:保存固定坐标 ​

需求:保存坐标 (10, 20),后续允许读取,但不允许直接替换其中的数值。

python
position = (10, 20)
print(position)

列表可以修改,元组创建后不能直接修改。

列表与元组可变性的对比图

元组创建 ​

除了坐标,城市列表、日期信息和固定配置也可能需要保持不变。元组通常使用 () 创建,元素之间用逗号分隔:

python
city_tuple = ("长沙", "深圳", "北京")
print(city_tuple)
print(type(city_tuple))

运行结果:

text
('长沙', '深圳', '北京')
<class 'tuple'>

也可以使用 tuple() 创建空元组,或者把其他可遍历内容转换成元组:

python
empty_tuple = tuple()
char_tuple = tuple("Python")

print(empty_tuple)
print(char_tuple)

运行结果:

text
()
('P', 'y', 't', 'h', 'o', 'n')

单元素元组 ​

如果固定配置中只有一个值,怎样确保它仍然是元组?元组只有一个元素时,元素后面必须保留逗号:

python
number_tuple = (1,)
number = (1)

print(number_tuple, type(number_tuple))
print(number, type(number))

运行结果:

text
(1,) <class 'tuple'>
1 <class 'int'>

(1) 只是给数字 1 加了一对括号,类型仍然是整数。(1,) 中的逗号才表示这是一个元组。

单元素元组必须保留逗号的示意图

元组索引、切片与遍历 ​

坐标或固定配置保存下来之后,仍然需要读取其中的某个值、取出一段数据或逐项处理。元组属于序列类型,取值和切片的写法与列表相同。

索引访问 ​

需要读取某个城市或坐标分量时,使用索引按位置取值:

python
city_tuple = ("长沙", "深圳", "北京")

print(city_tuple[0])
print(city_tuple[-1])

运行结果:

text
长沙
北京

元组索引也从 0 开始,-1 表示最后一个元素。索引超出范围时,同样会抛出 IndexError。

切片访问 ​

需要保留一段数据时,使用切片;切片只创建新元组,不会改变原数据:

python
number_tuple = (0, 1, 2, 3, 4, 5)

print(number_tuple[1:4])
print(number_tuple[:3])
print(number_tuple[::2])
print(number_tuple[::-1])

运行结果:

text
(1, 2, 3)
(0, 1, 2)
(0, 2, 4)
(5, 4, 3, 2, 1, 0)

切片会返回一个新元组,不会修改原元组。

元组双向索引与切片提取机制图

元组遍历 ​

需要依次处理所有城市时,使用 for 按顺序取出元组中的每个元素:

python
city_tuple = ("长沙", "深圳", "北京")

for city in city_tuple:
    print(city)

元组不可变性 ​

如果有人尝试把坐标中的第一个值改成其他数字,程序会发生什么?元组创建后,不能直接增加、删除或修改其中的元素。

下面的代码会触发错误:

python
city_tuple = ("长沙", "深圳", "北京")
city_tuple[0] = "上海"

运行后会抛出异常:

text
TypeError: 'tuple' object does not support item assignment

元组没有列表中的 append()、insert()、remove() 和 pop() 等修改方法。

如果业务确实需要调整内容,可以先转换成列表,修改后再转回元组:

python
city_tuple = ("长沙", "深圳", "北京")

city_list = list(city_tuple)
city_list[0] = "上海"
city_tuple = tuple(city_list)

print(city_tuple)

运行结果:

text
('上海', '深圳', '北京')

这不是在修改原元组,而是根据修改后的列表创建了一个新元组。

元组转换为列表、修改后再创建新元组的流程图

注意

“元组不能修改”指的是不能替换元组直接保存的元素。如果元组中保存了列表,列表内部的内容仍然可以修改:

python
data = ("课程", ["Python", "MySQL"])
data[1].append("Linux")

print(data)

元组查询操作 ​

元组不能修改,是否就不能查询?当然不是。仍然可以获取长度、查找位置、统计次数和判断元素是否存在:

操作作用
len(tuple_data)获取元素数量
tuple_data.index(data)查询元素第一次出现的索引
tuple_data.count(data)统计元素出现的次数
data in tuple_data判断元素是否存在
python
score_tuple = (90, 85, 90, 100)

print(len(score_tuple))
print(score_tuple.index(85))
print(score_tuple.count(90))
print(100 in score_tuple)

运行结果:

text
4
1
2
True

列表与元组的选择 ​

遇到一组数据时,关键问题是“后续是否允许修改”。列表和元组都能按顺序保存多个数据,但适用场景不同:

对比项列表 list元组 tuple
常用写法[1, 2, 3](1, 2, 3)
是否有顺序是是
是否支持索引、切片是是
是否可以修改可以不可以
常见场景内容需要增删改内容创建后保持不变

可以按下面的需求选择:

  • 学生名单后续还会增加或删除,使用列表
  • 地图坐标创建后不应该变化,使用元组

元组通常占用更少的内存,遍历速度也可能略快。但选择元组的首要原因不是性能,而是明确表达“这组数据不应该修改”。

只包含数字、字符串等不可变数据的元组,可以作为字典的键或集合的元素。如果元组中包含列表,就不能这样使用。字典和集合会在后面的章节学习。

小结 ​

本节先回答四个问题:

  1. 什么数据适合用元组?有顺序且创建后不应修改的数据
  2. 如何读取元组?使用索引、切片和 for 遍历
  3. 单元素元组如何表示?元素后必须保留逗号
  4. 元组需要调整时怎么办?转换为列表修改,再创建新元组;如果数据本来就要增删改,应直接使用列表

练习 ​

  1. 创建一个包含 3 个城市名称的元组
  2. 取出第一个元素、最后一个元素和前两个元素
  3. 使用 for 遍历并打印所有城市
  4. 尝试修改其中一个元素,观察 TypeError