前面我们已经学过列表。
列表很好用,能加、能删、能改,灵活得很。但有时候,太灵活反而不是好事。
比如一个人的出生日期,一旦确定下来,就不应该被程序随便改掉。 比如一组坐标数据,本身就应该是固定的。 比如函数返回多个结果时,我们只是想把它们打包带走,并不希望别人顺手改掉。
这时候,元组就比列表更合适。
很多新手第一次学元组,都会觉得它有点鸡肋。 既然列表已经够用了,为什么还要多学一个差不多的东西?
真正的原因只有一句话:
不是所有数据都应该被修改。
元组的价值,就藏在这个不可变里。
一、什么是元组
元组也是一种容器,可以一次装多个数据。
看起来和列表很像:
nums = (10, 20, 30, 40)print(nums)print(type(nums))
输出:
(10, 20, 30, 40)<class 'tuple'>
它和列表最大的区别,不是能不能装多个值,而是:
列表可变 元组不可变
也就是说,元组创建好之后,里面的元素不能直接修改、删除、添加。
你可以把它理解成:
列表像草稿纸,能反复擦改 元组像定稿,一旦写好,就别动了
二、元组长什么样
元组最常见的写法是用小括号:
info = ('张三', 18, '北京')
列表用中括号,元组用小括号。
这一点很好记:
[] 是列表() 是元组
不过,光记外表还不够。元组里有一个特别容易踩坑的地方,就是只有一个元素时的写法。
三、只有一个元素的元组,为什么要加逗号
先看代码:
a = (10)b = (10,)print(type(a))print(type(b))
输出:
<class 'int'><class 'tuple'>
你会发现:
(10) 不是元组,而是整数(10,) 才是元组
为什么?
因为 Python 判断一个东西是不是元组,关键不在小括号,而在逗号。
所以:
x = 1, 2, 3print(x)print(type(x))
输出:
(1, 2, 3)<class 'tuple'>
这说明就算不写小括号,只要有逗号,Python 也能把它识别成元组。
但在正式代码里,还是建议把括号写上,清楚、直观,不容易看花眼。
记住这一条就够了:
单个元素的元组,一定要写逗号。
四、元组能做什么
元组和列表一样,也能存放各种类型的数据。
data = ('Tom', 20, 99.5, True)print(data)
元组里也可以混合放字符串、数字、布尔值,甚至还能放列表、字典。
比如:
item = ('苹果', 5.8, ['红富士', '冰糖心'])print(item)
这说明元组本身不能改,但它里面如果套了一个可变对象,那里面的内容还是可能变化的。这个问题后面会单独说。
先把核心吃透:
元组是用来装一组值的,只不过这组值默认不希望被改来改去。
五、元组也支持索引和切片
很多人会误以为元组不能改,所以也不好取值。其实不是。
元组虽然不可变,但照样可以读取。
1. 索引取值
info = ('张三', 18, '北京')print(info[0])print(info[1])
输出:
张三18
2. 负数索引
print(info[-1])
输出:
北京
3. 切片
nums = (10, 20, 30, 40, 50)print(nums[1:4])
输出:
(20, 30, 40)
注意,切片之后得到的还是元组,不是列表。
所以你可以把元组理解成:
能看 能取 能遍历 但不能直接改
六、元组为什么不可变
这个问题问得很好。
因为很多新手一听不可变,第一反应就是:
那它是不是功能更少了?
表面看是少了点灵活性,但从设计角度看,不可变反而是一种保护。
它至少有三个好处。
1. 数据更安全
如果某些数据天生不该改,那用元组就能避免误操作。
比如身份证信息、颜色配置、坐标位置、数据库连接参数这些,通常更适合用元组或其他不可变结构保存。
比如:
point = (120, 30)
坐标就是坐标,不应该写着写着突然变成别的。
2. 语义更明确
当别人看到列表时,会自然认为这个数据后面可能会变。 但看到元组时,就知道这是一组固定信息。
这其实是在告诉阅读代码的人:
别动它,这不是拿来随便改的。
写代码不仅是让机器跑,也是在和未来的自己、和别的开发者交流。
元组在这方面很像一种态度声明。
3. 适合表示一组固定搭配的数据
比如一个学生的信息:
student = ('张三', 18, '男')
比如一个 RGB 颜色值:
color = (255, 128, 0)
比如年月日:
date = (2026, 3, 26)
这些数据不是不能变化,而是它们在一个具体场景里,通常被当成一个固定组合来看待。
元组就很适合表示这种固定结构。
七、元组和列表最核心的区别
把前面的内容压缩成一句最实用的话:
列表适合会变化的数据 元组适合固定不变的数据
来看一个对比例子。
用列表表示购物车商品:
cart = ['苹果', '香蕉', '牛奶']
购物车里的商品可能增删,当然用列表。
再看一个用户出生日期:
birthday = (2000, 5, 18)
出生日期本身就是固定信息,更适合元组。
所以,不是元组比列表高级,也不是列表比元组万能。 它们只是适合的场景不同。
八、元组不能改,到底会报什么错
这部分建议你亲手敲一遍,印象会非常深。
nums = (10, 20, 30)nums[1] = 200
运行后会报错:
TypeError
因为元组不支持按索引修改。
同样,下面这些操作也不行:
nums.append(40)nums.remove(20)del nums[0]
为什么?
因为这些操作本质上都在改元组,而元组是不可变的。
如果你只是读取,那没问题。 如果你想修改,Python 会直接拦住你。
这不是它笨,而是它故意不让你乱来。
九、元组可以遍历吗
当然可以。
元组虽然不能改,但完全可以循环读取。
colors = ('red', 'green', 'blue')for color in colors: print(color)
输出:
redgreenblue
如果你只需要把里面的数据一个个拿出来处理,元组和列表的使用体验几乎一样。
这一点也很关键:
不可变,不等于不能用。 它只是不能被改,不是不能被读。
十、元组常用方法很少,这反而是优点
列表的方法很多,比如 append、insert、remove、pop 等等。 元组的方法非常少,常用的几乎就两个:
count()index()
1. count() 统计出现次数
nums = (1, 2, 2, 3, 2, 4)print(nums.count(2))
输出:
3
2. index() 查找第一次出现的位置
nums = (10, 20, 30, 20)print(nums.index(20))
输出:
1
为什么元组的方法这么少?
因为它不能改,所以那些增删改的方法压根没必要存在。
你可以把这理解成一种极简设计:
只保留和读取相关的功能。
十一、元组拆包,是它特别好用的一点
元组在实际开发里,有一个非常舒服的用法,叫拆包。
先看:
person = ('张三', 18, '北京')name, age, city = personprint(name)print(age)print(city)
输出:
张三18北京
这就叫把一个元组里的多个值,一次性拆到多个变量里。
这种写法很自然,特别适合处理固定数量的数据。
比如交换两个变量的值:
a = 10b = 20a, b = b, aprint(a, b)
输出:
2010
这背后其实也和元组打包、拆包有关。
所以元组不是一个摆设,它在写简洁代码时,经常默默出力。
十二、函数返回多个值,本质上也是元组
看一个函数:
defget_user():return'张三', 18result = get_user()print(result)print(type(result))
输出:
('张三', 18)<class 'tuple'>
虽然写的时候没有显式写元组,但 Python 会自动把多个返回值打包成元组。
所以你以后经常会看到这种写法:
defget_point():return100, 200x, y = get_point()print(x)print(y)
这就是元组在函数里的典型应用。
很多人学元组时觉得它存在感不强,是因为它经常藏在语法背后,悄悄地帮你把数据打包好了。
十三、元组里放列表,算不算真的不可变
这个问题特别容易让人困惑。
看例子:
data = (10, 20, [30, 40])print(data)
现在试着改里面的列表:
data[2][0] = 300print(data)
输出:
(10, 20, [300, 40])
你会愣一下:
不是说元组不可变吗,怎么又改成功了?
关键在这里:
元组不可变,指的是元组内部每个元素的指向不能变。 但如果某个元素本身是可变对象,比如列表,那么列表内部内容还是可以改。
你不能这样做:
data[2] = [100, 200]
这会报错,因为你试图把第 3 个元素整个换掉。
但你可以修改第 3 个元素这个列表里的内容,因为那个列表自己是可变的。
这个点初学阶段能理解到这里就够用了。
一句话总结:
元组本身不可变 元组里的可变对象,内部仍然可变
十四、什么时候该用元组
这里给你几个很实用的判断标准。
如果你遇到的是下面这些场景,优先考虑元组:
1. 一组固定信息
比如学生基本信息、坐标、日期、颜色值。
student = ('李雷', 16, '高一')point = (120, 35)date = (2026, 3, 26)
2. 函数返回多个结果
defcalc(a, b):return a + b, a - b
3. 不希望别人随意改数据
如果某个数据逻辑上应该是只读的,那用元组比列表更合适。
4. 表达固定结构
比如一条记录总是由 姓名、年龄、城市 组成,这种固定格式很适合元组。
十五、什么时候不该用元组
反过来说,如果你的数据明显需要频繁变化,就别硬上元组。
比如:
购物车商品 待办事项列表 聊天消息记录 动态增加的分数集合
这些都是典型的可变数据,更适合列表。
比如:
tasks = ['写作业', '背单词', '跑步']tasks.append('复习数学')
这里如果你用元组,后面每加一项都很别扭。
所以,选容器不是看哪个语法更酷,而是看数据本身的性质。
十六、把元组转成列表,再修改
现实中有时候确实会遇到这种情况:
一开始是元组,后来临时想改一改。
那怎么办?
办法很简单,先转成列表,改完再看需不需要转回元组。
nums = (10, 20, 30)temp = list(nums)temp[1] = 200nums = tuple(temp)print(nums)
输出:
(10, 200, 30)
这也是处理不可变数据的常见思路:
不是硬改,而是先转成可变结构,再重新生成。
十七、一个生活化的理解方式
如果你一直觉得元组抽象,可以把它理解成表格里一行固定记录。
比如一条订单信息:
订单号 用户名 金额 下单时间
这一条记录在创建后,通常不会被你在程序里随手 append 一个新字段,也不会删掉中间某一项。 它更像一个结构固定的数据单元。
比如:
order = ('A1001', '小王', 99.9, '2026-03-26 10:30')
这种数据放到元组里,表达上就很合适。
反过来,如果是订单列表,那它会不断新增,这时候外层就更适合用列表。
比如:
orders = [ ('A1001', '小王', 99.9), ('A1002', '小李', 59.8), ('A1003', '小张', 188.0)]
你看,外层列表,内层元组,这就是很常见的一种搭配。
外层负责增减记录 内层负责表示每条固定结构的数据
这个组合在很多项目里都很自然。
十八、练习题:把元组真正用起来
下面几道题,建议你先自己写,再看答案。
1. 定义一个元组,保存你的姓名、年龄、城市,并打印出来
参考答案:
info = ('小明', 20, '杭州')print(info)
2. 取出元组中的第一个和最后一个元素
info = ('小明', 20, '杭州')print(info[0])print(info[-1])
3. 统计某个元素出现了几次
nums = (1, 2, 2, 3, 2, 4)print(nums.count(2))
4. 查找元素第一次出现的位置
nums = (10, 20, 30, 20)print(nums.index(20))
5. 把元组拆包到多个变量中
student = ('小红', 17, '上海')name, age, city = studentprint(name)print(age)print(city)
十九、本章小结
这一章最关键的,不是死记元组的语法,而是理解它为什么存在。
元组和列表都能装多个值。 但列表适合变化,元组适合固定。
你要记住的核心点有这几个:
元组用小括号表示 单个元素的元组必须加逗号 元组支持索引、切片、遍历 元组不能直接增删改 元组适合表示固定结构、只读数据、函数多返回值
最重要的一句,再说一次:
不可变,不是没用 恰恰是因为不该变,所以才有价值
下一章我们继续进入 字典 dict 入门:键值对才是现实世界的数据结构。 到那一章你会发现,前面的列表和元组更像排成一排的数据,而字典,才真正开始接近现实里的信息组织方式。