一、什么是元组(写下来就不许改)
元组(tuple)和列表长得像,也是用一串方括号……不对,是圆括号把多个值装起来。但它有个铁规矩:一旦创建,里面的内容就不能改。
打个比方:列表像个水果篮,随时能往里加苹果、拿走香蕉;元组像一份固定套餐,下单之后菜就定死了,不能临时换。
t = (1, 2, 3, 4, 5)
print(t)
print('长度是', len(t))
输出:
(1, 2, 3, 4, 5)
长度是 5
表面看它和列表差不多,但「不可改」这个特性,让它在很多场合比列表更合适(后面第十节「元组的常见用途」会讲)。
课程思政|数据安全与法治素养 · 原始记录,写了就不许改
元组的铁规矩是「一旦创建就不能改」,这点看着不如列表灵活,却正好对上统计工作的老规矩:原始台账一经登记,就不许偷偷回改。分数录错了要另写一份更正说明,而不是把原来的数字划掉重填。能改的是副本,不能改的是底账。把该锁死的数据锁死,比事后一笔笔追查省力得多。
二、创建元组的四种方式
方式一:圆括号直接写
t = (1, 2, 3)
print(t)
输出:
(1, 2, 3)
方式二:单个元素——逗号不能少!
只写一个元素时,必须加逗号,否则会被当成普通括号里的表达式:
a = (1)
b = (1,)
print('a 的类型:', type(a))
print('b 的类型:', type(b))
输出:
a 的类型: <class 'int'>
b 的类型: <class 'tuple'>
(1) 只是给数字 1 套了层没用的括号,结果还是整数;(1,) 才是只有一个元素的元组。
方式三:用 tuple() 把别的序列转成元组
t = tuple([4, 5, 6])
print(t)
输出:
(4, 5, 6)
方式四:连括号都不写也行
用逗号隔开的一串值,Python 会自动当成元组:
t = 1, 2, 3
print(t)
print('类型:', type(t))
输出:
(1, 2, 3)
类型: <class 'tuple'>
| 写法 | 结果 | 说明 |
|---|---|---|
(1, 2, 3) | 元组 | 最常用,圆括号 |
(1,) | 只有一个元素的元组 | 逗号不能省 |
tuple([4,5,6]) | 元组 | 把列表等转成元组 |
1, 2, 3 | 元组 | 不写括号也行,逗号决定它是元组 |
(1,)。漏掉逗号,(1) 只是整数 1,不是元组——后面传到需要元组的地方就会报错。type() 看一眼最稳妥:type((1,)) 得到 tuple,type((1)) 得到 int。三、用索引和切片访问元素
元组和列表一样,靠索引取值,从 0 开始;负数从右边数。
t = (10, 20, 30)
print(t[0])
print(t[-1])
print(t[1])
输出:
10
30
20
切片:一次拿一段
[起点:终点] 同样是含起点、不含终点:
t = (10, 20, 30, 40, 50)
print(t[1:4])
print(t[::-1])
输出:
(20, 30, 40)
(50, 40, 30, 20, 10)
t[::-1] 里的步长写 -1,表示倒着一个一个取,于是把整个元组反转过来。
四、遍历元组
元组也能用 for 循环一个一个取出来,方式和列表一模一样:
t = ('苹果', '香蕉', '橙子')
for x in t:
print(x, end=" ")
输出:
苹果 香蕉 橙子
也能用 for 配合 range() 按索引遍历:
t = ('春', '夏', '秋', '冬')
for i in range(len(t)):
print(i, t[i])
输出:
0 春
1 夏
2 秋
3 冬
for 元素 in 元组,最简洁。五、元组的内置方法与常用函数
元组因为「不能改」,它自带的方法比列表少得多——只有两个:
| 方法 | 作用 | 例子 |
|---|---|---|
t.count(x) | 数一数 x 出现了几次 | (5, 8, 5).count(5) → 2 |
t.index(x) | 找 x 第一次出现的下标 | (5, 8, 5).index(8) → 1 |
t = (5, 8, 5, 2)
print(t.count(5)) # 5 出现了两次
print(t.index(8)) # 8 在第 2 个位置
输出:
2
1
找不到的时候,index 会直接报错:
t = (5, 8, 5, 2)
print(t.index(99))
运行后会报错(逐字照抄解释器):
ValueError: tuple.index(x): x not in tuple
除此之外,还有一批「通用内置函数」——它们不挑类型,凡是序列都能用:
| 函数 | 作用 |
|---|---|
len(t) | 一共几个元素 |
max(t) / min(t) | 最大 / 最小 |
sum(t) | 求和(元素必须都是数字) |
sorted(t) | 从小到大排好序 |
any(t) / all(t) | 有没有真的 / 是不是全都是真的 |
x in t | x 在不在里面 |
t = (7, 3, 9, 1)
print(len(t))
print(max(t), min(t))
print(sum(t))
print(sorted(t))
print(type(sorted(t)))
print(5 in t)
输出:
4
9 1
20
[1, 3, 7, 9]
<class 'list'>
False
sorted(t) 的类型——它返回的是列表,不是元组(因为元组不能改,没法在原地排序)。想要元组就再包一层:tuple(sorted(t))。append、remove、sort)都没有。课程思政|工匠精神与严谨求实 · 会得少,也要用得准
元组因为不能改,自带的方法只有 count 和 index 两个,比列表少得多;但配上 len、max、sum 这些通用函数,日常照样够用。而且 index 找不到值时会直接报错,所以老手习惯先用 in 判断一下。工具不在多,在于用对——把手上这些吃透,比追着新花样跑踏实得多。
六、元组和列表的相同点
在「读取」这件事上,元组和列表几乎是一家人。下面这些操作两者都支持:
| 相同的能力 | 例子 | 效果 |
|---|---|---|
| 按索引取值 | t[0] | 拿第 1 个 |
| 负索引 | t[-1] | 拿最后一个 |
| 切片 | t[1:3] | 拿一段 |
| 求长度 | len(t) | 有几个元素 |
| 用 for 遍历 | for x in t | 逐个取出 |
| 用 in 判断在不在 | 2 in t | 返回 True / False |
t = (1, 2, 3)
print(2 in t)
print(len(t))
输出:
True
3
所以:凡是只看不改的场景,元组能完全替代列表。
AI 助手小贴士|「哪些操作元组没有」用一张表说清楚,比逐个记牢靠。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:整理一张表:列表和元组都支持哪些操作、哪些操作只有列表有?每行给出原因(和可变性有什么关系)
七、元组和列表的不同点(可变性 / 括号 / 性能 / 能不能当字典键)
真正的差别在「能不能改」以及由此带来的一些后果:
| 对比项 | 列表 list | 元组 tuple |
|---|---|---|
| 符号 | 方括号 [] | 圆括号 () |
| 能不能改元素 | 能(append / 改 / 删) | 不能,改了就报错 |
| 性能 | 稍慢一点 | 稍快、占内存更少 |
| 能不能当字典的键 | 不能(可变,不可哈希) | 能(不可变,可哈希;前提是里面装的元素也都不可变) |
| 典型用途 | 一变再变的数据 | 固定不变的数据 |
t = (1, 2, 3)
lst = [1, 2, 3]
print('元组能当字典键:', {t: 'ok'})
print('列表不能当字典键(下面会报错)')
输出:
元组能当字典键: {(1, 2, 3): 'ok'}
列表不能当字典键(下面会报错)
关于「能不能当字典的键」:字典的键要求不可变,元组满足,列表不满足(列表那条会报错,见报错手册)。
八、元组改不了,但可以「重造」(+ 拼接、* 重复、list 转换)
元组不能直接改,但你可以基于旧的,造一个新的。就像套餐换不了菜,但可以「照原套餐再加个甜点」点一份新的。
用 + 拼出新元组
t = (1, 2, 3)
t2 = t + (4,)
print(t2)
print('原来的 t 没变:', t)
输出:
(1, 2, 3, 4)
原来的 t 没变: (1, 2, 3)
注意 (4,) 那个逗号——右边必须是元组,单元素元组逗号不能省。+ 产生的是新元组,原来的 t 一动没动。
用 * 重复
t = (1, 2)
print(t * 3)
输出:
(1, 2, 1, 2, 1, 2)
先转成列表改,再转回元组
如果真要改很多,可以先 list() 变列表、改完、再 tuple() 变回元组:
t = (1, 2, 3)
lst = list(t)
lst.append(4)
t = tuple(lst)
print(t)
输出:
(1, 2, 3, 4)
+ 和 * 都不会改动原元组,而是返回一个新元组。如果想「更新」t,记得把结果重新赋值给 t(像上面 t = t + (4,))。九、元组推导式?其实并没有
列表有列表推导式,字典有字典推导式,集合也有集合推导式……你可能会想:那元组有没有「元组推导式」?
答案是——没有。如果你写成圆括号的推导式,得到的并不是元组,而是一个生成器:
g = (x * x for x in range(1, 4))
print(type(g))
print(tuple(g))
输出:
<class 'generator'>
(1, 4, 9)
看类型就明白了:打出来是 generator,不是 tuple。生成器是一种「用到才算」的东西,能被 for 遍历,但它本身不是一个元组。
tuple(g) 就是把做出来的每一杯都装进打包袋。想造元组:外面套一层 tuple()
print(tuple(x * x for x in range(1, 6)))
print(tuple([x * x for x in range(1, 6)]))
print(tuple('abc'))
输出:
(1, 4, 9, 16, 25)
(1, 4, 9, 16, 25)
('a', 'b', 'c')
上面三种写法都能得到元组:第一种是「生成器 + tuple()」,第二种是「列表推导式 + tuple()」,第三种是把字符串转成元组。
| 推导式写法 | 得到什么 |
|---|---|
[x for x in ...] | 列表 list |
{x for x in ...} | 集合 set |
{k: v for ...} | 字典 dict |
(x for x in ...) | 生成器,不是元组! |
tuple(x for x in ...) | 元组 tuple |
(x for x in ...) 千万别以为造出了元组——它连 len() 都用不了。tuple(...) 包一层;只想「边算边用、不占内存」,那就直接用生成器。十、元组的常见用途(坐标、固定配置、函数返回多值、字典的键)
正因为「不可变」,元组在下面这些地方特别好用:
1. 表示坐标、颜色等固定组合
point = (120, 80)
color = (255, 0, 0)
print('x =', point[0], 'y =', point[1])
输出:
x = 120 y = 80
2. 固定配置(比如窗口尺寸)
WINDOW_SIZE = (800, 600)
print('宽', WINDOW_SIZE[0], '高', WINDOW_SIZE[1])
输出:
宽 800 高 600
3. 函数一口气返回多个值
函数返回多个值时,其实返回的是一个元组:
def min_max(nums):
return min(nums), max(nums)
result = min_max([3, 1, 4, 1, 5])
print(result)
print('类型是', type(result))
输出:
(1, 5)
类型是 <class 'tuple'>
4. 当字典的键(组合键)
score = {('小明', '数学'): 90, ('小红', '语文'): 85}
print(score[('小明', '数学')])
输出:
90
5. 装一串不会变的固定内容
二十四节气的顺序是老祖宗定下来的,一年到头不会变——这种「定死的清单」正适合用元组装:
terms = ('立春', '雨水', '惊蛰', '春分', '清明', '谷雨')
print(terms)
print('春天有', len(terms), '个节气')
print('清明' in terms)
输出:
('立春', '雨水', '惊蛰', '春分', '清明', '谷雨')
春天有 6 个节气
True
凡是「写下来就不会再改」的一串东西——节气、月份名、菜单上的固定套餐——都可以放心交给元组。
AI 助手小贴士|元组的典型用途里藏着「为什么需要它」的答案。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:为什么函数返回多个值时推荐用元组而不是列表?结合坐标 (x, y) 这种「位置固定、含义成对」的例子讲
课程思政|诚信与职业操守 · 一条数据要能对上人
元组常被拿来当字典的「组合键」,比如把 (姓名, 科目) 拧成一把钥匙,再去查对应的分数。这样查表又快又准,绝不会把两个同名同学的分数混在一起。数据必须能对上具体的人。统计里最怕的不是算错,而是张冠李戴——数字本身没错,却挂在了别人名下,那才叫失职。
十一、十个基础案例
下面十个例子都很短,建议每个都亲手敲一遍、跑一遍。
1. 创建一个「奶茶订单」元组(字符和数字混着放)
t = ('奶茶', '三分糖', 12.5)
print(t)
输出:
('奶茶', '三分糖', 12.5)
2. 取快递架上的最后一个取件码
codes = ('A1', 'B2', 'C3', 'D4')
print(codes[-1])
输出:
D4
3. 把课表倒过来看一眼
t = ('语文', '数学', '英语', '体育')
print(t[::-1])
输出:
('体育', '英语', '数学', '语文')
4. 把列表转成元组(锁住不让改)
lst = ['奶茶', '炸鸡', '薯条']
t = tuple(lst)
print(t)
输出:
('奶茶', '炸鸡', '薯条')
5. 用 + 拼接两份课表
a = ('数学', '英语')
b = ('体育', '美术')
print(a + b)
输出:
('数学', '英语', '体育', '美术')
6. 解包:把快递信息拆成变量
info = ('菜鸟驿站', 'A-16')
place, code = info
print('地点', place, '取件码', code)
输出:
地点 菜鸟驿站 取件码 A-16
7. 用 in 判断口味在不在菜单
menu = ('原味', '芝士', '抹茶')
print('芝士' in menu)
print('榴莲' in menu)
输出:
True
False
8. 数一数有几种小料
t = ('珍珠', '椰果', '布丁', '仙草')
print(len(t))
输出:
4
9. 用 for 遍历课表并带上序号
t = ('周一', '周二', '周三', '周四')
for i, v in enumerate(t):
print(i, v)
输出:
0 周一
1 周二
2 周三
3 周四
10. 用(出发站, 终点站)当字典的键
d = {('北京', '上海'): 553}
print(d[('北京', '上海')], '元')
输出:
553 元
a, b = func() 一次接住。解包这招在序列解包那篇还会专门讲。十二、常见坑(含「元组里的列表可以改」)
坑 1:单元素元组忘了逗号
a = (1)
print('a 其实是', type(a))
输出:
a 其实是 <class 'int'>
想写只有一个元素的元组,必须 (1,),少了逗号就成了普通的整数。
坑 2:元组不能改,但「里面的列表」可以改
元组自己不可变,但如果某个元素是个列表,那个列表内部还能动:
t = (1, [2, 3])
t[1].append(4)
print(t)
输出:
(1, [2, 3, 4])
这是因为元组锁住的是「第 1 个元素是个列表」这件事,并没锁住那个列表里面的内容。所以别误以为元组里的一切都动不了。
坑 3:想用 append 往元组加东西
元组没有 append,写了会报 AttributeError(见报错手册)。要加东西,用 + 拼出新元组。
坑 4:以为 + 会改原元组
t = (1, 2, 3)
t + (4,)
print('t 原样没变:', t)
输出:
t 原样没变: (1, 2, 3)
t + (4,) 算出了一个新元组,但没赋值回 t,所以 t 还是老样子。要更新就写 t = t + (4,)。
list() 转列表 → 增删改 → 再 tuple() 转回。这是处理「需要改但又想用元组」的标准流程。十三、报错手册
下面这些报错都是本机真实运行出来的,照抄解释器原文。
改元素:TypeError
t = (1, 2, 3)
t[1] = 10
运行后会报错(逐字照抄解释器):
TypeError: 'tuple' object does not support item assignment
原因:元组不可变,不能给某个位置重新赋值。
删元素:TypeError
t = (1, 2, 3)
del t[1]
运行后会报错(逐字照抄解释器):
TypeError: 'tuple' object doesn't support item deletion
原因:同样因为不可变,连删除都不行。
用 append:AttributeError
t = (1, 2, 3)
t.append(4)
运行后会报错(逐字照抄解释器):
AttributeError: 'tuple' object has no attribute 'append'
原因:元组没有 append 这个方法(那是列表的)。要加元素用 t + (4,)。
索引越界:IndexError
t = (1, 2, 3)
print(t[10])
运行后会报错(逐字照抄解释器):
IndexError: tuple index out of range
原因:元组只有 3 个元素(索引 0~2),取第 10 个超出范围。
把非可迭代对象转元组:TypeError
print(tuple(5))
运行后会报错(逐字照抄解释器):
TypeError: 'int' object is not iterable
原因:tuple() 需要一个能一个一个取出来的东西(列表、字符串等)。数字 5 不是,会报「int 对象不可迭代」。
列表当字典键:TypeError
d = {[1, 2]: 'a'}
运行后会报错(逐字照抄解释器):
TypeError: unhashable type: 'list'
原因:字典的键必须不可变(可哈希)。列表是可变的,不能当键;换成元组 {(1, 2): 'a'} 就正常。
object does not support item assignment(不能改)、has no attribute 'append'(没这方法)、unhashable type: 'list'(列表不能当键)。看到这类词,先想「是不是动了不该动的东西」。AI 助手小贴士|「已经有列表了,为什么还要元组」是高频疑问——这类设计动机问题,AI 特别擅长答。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:Python 已经有列表了,为什么还要元组?举 3 个真实项目里「该用元组」的场景,并说明理由
十四、随堂自测(12 题)
点击选项即可判分,选错的题会立刻展开解析。做完可以看到正确率和错题清单,也可以重新答题。
1元组在 Python 里通常用哪种括号表示?
- A.
[] - B.
() - C.
{} - D.
<>
() 表示,例如 (1, 2, 3)。方括号是列表,花括号是字典或集合。2t = (1) 这个表达式里,t 的类型是什么?
- A. int(整数)
- B. tuple(元组)
- C. str(字符串)
- D. 会报错
(1) 只是一层没用的括号,结果还是整数 1。type((1)) 得到 int,只有 (1,) 才是只有一个元素的元组。3下面哪个操作会修改元组并导致报错?
- A. 用
t[0]读取第一个元素 - B. 用
t[1:3]切片 - C. 用
for遍历 - D. 用
t[0] = 1改第一个元素
t[0] = 1 会报 'tuple' object does not support item assignment。读取、切片、遍历都是只读操作,不会报错。4t = (1, [2, 3]) 执行 t[1].append(4) 后,t 变成?
- A.
(1, [2, 3]) - B.
(1, [2, 3, 4]) - C. 报错
- D.
(1, 2, 3, 4)
t[1].append(4) 把那个列表变成了 [2,3,4],总体是 (1, [2, 3, 4])。5t = (1, 2, 3),想把它倒过来得到 (3, 2, 1),该用?
- A.
t.sort() - B.
t.reverse() - C.
t[::-1] - D.
sorted(t)
-1 即 t[::-1],直接得到反转的元组。元组没有 sort/reverse 方法;sorted(t) 返回的是列表不是元组。6t = (1, 2, 3),执行 t2 = t + (4,) 之后,原来的 t 是?
- A.
(1, 2, 3, 4) - B.
(1, 2, 3) - C. None
- D. 报错
+ 不会改原元组,而是造出一个新元组 (1,2,3,4) 赋给 t2;t 原样不变,仍是 (1, 2, 3)。7tuple([1, 2, 3]) 的结果是?
- A.
[1, 2, 3] - B.
{1, 2, 3} - C.
(1, 2, 3) - D. 报错
tuple() 把列表转成元组,结果是 (1, 2, 3)。反过来 list() 能把元组转回列表。8函数 def f(): return 1, 2,调用 f() 得到的返回值类型是什么?
- A. int
- B. list
- C. tuple
- D. str
(1, 2)。所以拿到后可以用 a, b = f() 一次性接住。9要遍历元组 t 里的每个元素,正确的写法是?
- A.
for x in t: - B.
for x in range(t): - C.
while t: - D. 元组不能遍历
for 元素 in 元组 遍历。range() 只收整数,不能写 range(t)。10t = (1, 2, 3),想把它变成列表再修改,正确做法是?
- A.
t.append(4) - B.
t[0] = 4 - C.
t.clear() - D.
list(t) 然后改列表
append、不能下标赋值、也没有 clear。正确姿势是 lst = list(t) 转成列表改完,再 tuple(lst) 转回。11元组 t = (5, 8, 5, 2),那么 t.count(5) 的结果是?
- A.
2 - B.
0 - C.
1 - D.
3
count(x) 数元素 x 出现了几次。5 在下标 0 和 2 各一次,所以是 2。想找位置用 t.index(5),返回第一次出现的下标 0。12(x * x for x in range(3)) 得到的是什么?
- A. 一个元组
(0, 1, 4) - B. 一个列表
[0, 1, 4] - C. 一个集合
{0, 1, 4} - D. 一个生成器(不是元组)
generator。要元组就写 tuple(x * x for x in range(3))。