Python3 元组(Tuple):一个上了锁的箱子
我是陈默,一个正拼命上岸的码农。
你有没有过这种经历?
辛辛苦苦整理好的数据,被一段代码不小心改掉了。找 bug 找了半天,最后发现是一个变量被意外修改。
元组就是 Python 给你的解决方案——一个上了锁的箱子。
装进去的东西,再也改不了。
1. 创建元组:把东西锁进箱子
元组用圆括号 () 表示,元素之间用逗号隔开。
# 创建元组coordinates = (3, 5)colors = ("红", "绿", "蓝")mixed = (1, "hello", 3.14, True)# 空元组empty = ()# 只有一个元素的元组(注意逗号!)single = (42,)print(type(coordinates)) # 输出: <class 'tuple'>print(coordinates) # 输出: (3, 5)
那个逗号很重要。
a = (42) # 这不是元组,只是个数字,加了括号而已b = (42,) # 这才是元组print(type(a)) # 输出: <class 'int'>print(type(b)) # 输出: <class 'tuple'>
这是初学者踩得最多的坑,没有之一。
2. 元组不可修改:上了锁就是上了锁
元组和列表最大的区别——元组创建之后,不能增、删、改。
fruits = ("苹果", "香蕉", "橙子")# 修改?不行fruits[0] = "芒果"# 报错!TypeError: 'tuple' object does not support item assignment# 添加?不行fruits.append("葡萄")# 报错!AttributeError: 'tuple' object has no attribute 'append'# 删除?不行del fruits[0]# 报错!TypeError: 'tuple' object doesn't support item deletion
看起来很不方便,对吧?
但这恰恰是它的价值——你把数据放进元组,就不用担心它被意外修改。
3. 访问元组:和列表一模一样
虽然不能改,但读完全没问题。
索引
fruits = ("苹果", "香蕉", "橙子", "葡萄", "西瓜")print(fruits[0]) # 输出: 苹果print(fruits[2]) # 输出: 橙子print(fruits[-1]) # 输出: 西瓜
切片
fruits = ("苹果", "香蕉", "橙子", "葡萄", "西瓜")print(fruits[1:3]) # 输出: ('香蕉', '橙子')print(fruits[:3]) # 输出: ('苹果', '香蕉', '橙子')print(fruits[2:]) # 输出: ('橙子', '葡萄', '西瓜')print(fruits[::-1]) # 输出: ('西瓜', '葡萄', '橙子', '香蕉', '苹果')
你看,索引和切片的语法,跟列表完全一样。
4. 元组的操作
虽然元组本身不能改,但你可以做一些"不修改"的操作。
拼接
t1 = (1, 2, 3)t2 = (4, 5, 6)result = t1 + t2print(result) # 输出: (1, 2, 3, 4, 5, 6)
注意:这不是修改原来的元组,而是创建了一个新元组。
重复
t = ("哈",)print(t * 5) # 输出: ('哈', '哈', '哈', '哈', '哈')
统计和查找
nums = (1, 3, 5, 3, 7, 3, 9)print(len(nums)) # 输出: 7(长度)print(nums.count(3)) # 输出: 3(3 出现了 3 次)print(nums.index(7)) # 输出: 4(7 的位置)print(5in nums) # 输出: Trueprint(max(nums)) # 输出: 9print(min(nums)) # 输出: 1print(sum(nums)) # 输出: 31
5. 元组解包:一次取出所有值
这是元组最优雅的用法。
基本解包
point = (3, 5)x, y = pointprint(x) # 输出: 3print(y) # 输出: 5
一行代码,把元组里的值分别赋给变量。
多变量解包
person = ("陈默", 25, "程序员")name, age, job = personprint(name) # 输出: 陈默print(age) # 输出: 25print(job) # 输出: 程序员
用 * 收集多余的值
scores = (85, 92, 78, 90, 67)first, *rest = scoresprint(first) # 输出: 85print(rest) # 输出: [92, 78, 90, 67]first, *middle, last = scoresprint(first) # 输出: 85print(middle) # 输出: [92, 78, 90]print(last) # 输出: 67
交换两个变量
不用临时变量,一行搞定:
a = 10b = 20a, b = b, aprint(a) # 输出: 20print(b) # 输出: 10
这背后其实就是元组解包。b, a 先打包成 (20, 10),再分别赋给 a 和 b。
6. 元组 vs 列表:什么时候用哪个?
什么时候用元组?
- 数据不会变,比如坐标
(x, y)、RGB 颜色 (255, 0, 0)
什么时候用列表?
简单判断:能不用列表就不用列表,能用元组就用元组。
7. 元组和函数:天生一对
函数返回多个值时,Python 默认用元组:
defget_user_info(): name = "陈默" age = 25 job = "程序员"return name, age, job # 其实返回的是一个元组result = get_user_info()print(type(result)) # 输出: <class 'tuple'>print(result) # 输出: ('陈默', 25, '程序员')# 直接解包接收name, age, job = get_user_info()print(name) # 输出: 陈默print(age) # 输出: 25print(job) # 输出: 程序员
你之前可能一直在用这个特性,只是不知道背后是元组。
8. 实战:用元组管理配置信息
# 数据库配置(这些值不应该被意外修改)DB_CONFIG = ("localhost", 3306, "root", "my_database")host, port, user, database = DB_CONFIGprint(f"连接地址:{host}")print(f"端口:{port}")print(f"用户名:{user}")print(f"数据库:{database}")# 输出:# 连接地址:localhost# 端口:3306# 用户名:root# 数据库:my_database
用元组存配置信息,代码里任何地方都改不了它。
这就是"防呆设计"——不是防别人,是防未来的自己。
最后
元组看起来不如列表灵活,但它的"不可变"恰恰是最大的优点。
记住三件事:
- 单元素元组必须加逗号:
(42,) 而不是 (42)
我的建议:
回头看看你之前写的代码,有没有哪些列表其实从来不需要修改?试着把它们改成元组。你会发现,代码变得更安全了。
好的代码不是能做更多事,而是让出错变得更难。
今天就到这里。
我是陈默,我们下期再见。
如果你觉得这篇文章有帮助,欢迎关注我。我会持续分享 Python 学习的干货。