当前位置:首页>python>《Python 从入门到精通》034|元组 tuple:为什么不可变也很重要

《Python 从入门到精通》034|元组 tuple:为什么不可变也很重要

  • 2026-10-11 08:29:10
《Python 从入门到精通》034|元组 tuple:为什么不可变也很重要

前面我们已经学过列表。

列表很好用,能加、能删、能改,灵活得很。但有时候,太灵活反而不是好事。

比如一个人的出生日期,一旦确定下来,就不应该被程序随便改掉。 比如一组坐标数据,本身就应该是固定的。 比如函数返回多个结果时,我们只是想把它们打包带走,并不希望别人顺手改掉。

这时候,元组就比列表更合适。

很多新手第一次学元组,都会觉得它有点鸡肋。 既然列表已经够用了,为什么还要多学一个差不多的东西?

真正的原因只有一句话:

不是所有数据都应该被修改。

元组的价值,就藏在这个不可变里。

一、什么是元组

元组也是一种容器,可以一次装多个数据。

看起来和列表很像:

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 入门:键值对才是现实世界的数据结构。 到那一章你会发现,前面的列表和元组更像排成一排的数据,而字典,才真正开始接近现实里的信息组织方式。

最新文章

随机文章