今天你 AI 赋能了吗?打开浏览器,在地址栏输入 xuefulu.ai ,按下 Enter 即可访问学府路1号!
2026年10月14日 星期三 10:40:16
常用网址: | | 学府路1号 https://xuefulu.ai | 下载 Python https://xuefulu.ai/Python.html | 下载 TRAE https://xuefulu.ai/Trae.html

一、什么是元组(写下来就不许改)

元组(tuple)和列表长得像,也是用一串方括号……不对,是圆括号把多个值装起来。但它有个铁规矩:一旦创建,里面的内容就不能改。

打个比方:列表像个水果篮,随时能往里加苹果、拿走香蕉;元组像一份固定套餐,下单之后菜就定死了,不能临时换。

Python
t = (1, 2, 3, 4, 5)
print(t)
print('长度是', len(t))

输出:

输出
(1, 2, 3, 4, 5)
长度是 5

表面看它和列表差不多,但「不可改」这个特性,让它在很多场合比列表更合适(后面第十节「元组的常见用途」会讲)。

记住一句话:元组是「写下来就不许改」的列表。当你有一组固定不变的数据(比如一年中 12 个月、RGB 三个颜色分量),就该用元组。

课程思政|数据安全与法治素养 · 原始记录,写了就不许改

元组的铁规矩是「一旦创建就不能改」,这点看着不如列表灵活,却正好对上统计工作的老规矩:原始台账一经登记,就不许偷偷回改。分数录错了要另写一份更正说明,而不是把原来的数字划掉重填。能改的是副本,不能改的是底账。把该锁死的数据锁死,比事后一笔笔追查省力得多。

二、创建元组的四种方式

方式一:圆括号直接写

Python
t = (1, 2, 3)
print(t)

输出:

输出
(1, 2, 3)

方式二:单个元素——逗号不能少!

只写一个元素时,必须加逗号,否则会被当成普通括号里的表达式:

Python
a = (1)
b = (1,)
print('a 的类型:', type(a))
print('b 的类型:', type(b))

输出:

输出
a 的类型: <class 'int'>
b 的类型: <class 'tuple'>

(1) 只是给数字 1 套了层没用的括号,结果还是整数;(1,) 才是只有一个元素的元组。

方式三:用 tuple() 把别的序列转成元组

Python
t = tuple([4, 5, 6])
print(t)

输出:

输出
(4, 5, 6)

方式四:连括号都不写也行

用逗号隔开的一串值,Python 会自动当成元组:

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 开始;负数从右边数。

Python
t = (10, 20, 30)
print(t[0])
print(t[-1])
print(t[1])

输出:

输出
10
30
20

切片:一次拿一段

[起点:终点] 同样是含起点、不含终点:

Python
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 循环一个一个取出来,方式和列表一模一样:

Python
t = ('苹果', '香蕉', '橙子')
for x in t:
    print(x, end=" ")

输出:

输出
苹果 香蕉 橙子 

也能用 for 配合 range() 按索引遍历:

Python
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
Python
t = (5, 8, 5, 2)
print(t.count(5))      # 5 出现了两次
print(t.index(8))      # 8 在第 2 个位置

输出:

输出
2
1

找不到的时候,index 会直接报错:

Python
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 tx 在不在里面
Python
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
Python
t = (1, 2, 3)
print(2 in t)
print(len(t))

输出:

输出
True
3

所以:凡是只看不改的场景,元组能完全替代列表。

判断该用元组还是列表,就看「这个数据创建后会不会变」。会变→列表;固定不变→元组。读取操作两者通用,不用纠结。

AI 助手小贴士|「哪些操作元组没有」用一张表说清楚,比逐个记牢靠。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:整理一张表:列表和元组都支持哪些操作、哪些操作只有列表有?每行给出原因(和可变性有什么关系)

七、元组和列表的不同点(可变性 / 括号 / 性能 / 能不能当字典键)

真正的差别在「能不能改」以及由此带来的一些后果:

对比项列表 list元组 tuple
符号方括号 []圆括号 ()
能不能改元素能(append / 改 / 删)不能,改了就报错
性能稍慢一点稍快、占内存更少
能不能当字典的键不能(可变,不可哈希)能(不可变,可哈希;前提是里面装的元素也都不可变)
典型用途一变再变的数据固定不变的数据
Python
t = (1, 2, 3)
lst = [1, 2, 3]
print('元组能当字典键:', {t: 'ok'})
print('列表不能当字典键(下面会报错)')

输出:

输出
元组能当字典键: {(1, 2, 3): 'ok'}
列表不能当字典键(下面会报错)

关于「能不能当字典的键」:字典的键要求不可变,元组满足,列表不满足(列表那条会报错,见报错手册)。

性能那点差距对咱们的小练习可以忽略不计,真正影响选择的是「语义」:用元组=明摆着告诉读代码的人「这串数据不会变」,更安全、更好懂。

八、元组改不了,但可以「重造」(+ 拼接、* 重复、list 转换)

元组不能直接改,但你可以基于旧的,造一个新的。就像套餐换不了菜,但可以「照原套餐再加个甜点」点一份新的。

用 + 拼出新元组

Python
t = (1, 2, 3)
t2 = t + (4,)
print(t2)
print('原来的 t 没变:', t)

输出:

输出
(1, 2, 3, 4)
原来的 t 没变: (1, 2, 3)

注意 (4,) 那个逗号——右边必须是元组,单元素元组逗号不能省。+ 产生的是新元组,原来的 t 一动没动。

用 * 重复

Python
t = (1, 2)
print(t * 3)

输出:

输出
(1, 2, 1, 2, 1, 2)

先转成列表改,再转回元组

如果真要改很多,可以先 list() 变列表、改完、再 tuple() 变回元组:

Python
t = (1, 2, 3)
lst = list(t)
lst.append(4)
t = tuple(lst)
print(t)

输出:

输出
(1, 2, 3, 4)
+ 和 * 都不会改动原元组,而是返回一个新元组。如果想「更新」t,记得把结果重新赋值给 t(像上面 t = t + (4,))。
元组「不可变」指的是:它的元素引用不能换。但后面会看到——如果某个元素本身是可变的(比如列表),那个元素里面的内容还是可以动的。

九、元组推导式?其实并没有

列表有列表推导式,字典有字典推导式,集合也有集合推导式……你可能会想:那元组有没有「元组推导式」?

答案是——没有。如果你写成圆括号的推导式,得到的并不是元组,而是一个生成器:

Python
g = (x * x for x in range(1, 4))
print(type(g))
print(tuple(g))

输出:

输出
<class 'generator'>
(1, 4, 9)

看类型就明白了:打出来是 generator,不是 tuple。生成器是一种「用到才算」的东西,能被 for 遍历,但它本身不是一个元组。

可以把生成器想成现做奶茶:列表是提前做好 10 杯摆在那儿;生成器是你要一杯它才做一杯,不存货。tuple(g) 就是把做出来的每一杯都装进打包袋。

想造元组:外面套一层 tuple()

Python
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. 表示坐标、颜色等固定组合

Python
point = (120, 80)
color = (255, 0, 0)
print('x =', point[0], 'y =', point[1])

输出:

输出
x = 120 y = 80

2. 固定配置(比如窗口尺寸)

Python
WINDOW_SIZE = (800, 600)
print('宽', WINDOW_SIZE[0], '高', WINDOW_SIZE[1])

输出:

输出
宽 800 高 600

3. 函数一口气返回多个值

函数返回多个值时,其实返回的是一个元组:

Python
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. 当字典的键(组合键)

Python
score = {('小明', '数学'): 90, ('小红', '语文'): 85}
print(score[('小明', '数学')])

输出:

输出
90

5. 装一串不会变的固定内容

二十四节气的顺序是老祖宗定下来的,一年到头不会变——这种「定死的清单」正适合用元组装:

Python
terms = ('立春', '雨水', '惊蛰', '春分', '清明', '谷雨')
print(terms)
print('春天有', len(terms), '个节气')
print('清明' in terms)

输出:

输出
('立春', '雨水', '惊蛰', '春分', '清明', '谷雨')
春天有 6 个节气
True

凡是「写下来就不会再改」的一串东西——节气、月份名、菜单上的固定套餐——都可以放心交给元组。

把「姓名, 科目」这种组合包成元组当字典键,是处理二维查表(谁、哪门课、多少分)的常用套路。列表做不到,因为它可变、不能哈希。

AI 助手小贴士|元组的典型用途里藏着「为什么需要它」的答案。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:为什么函数返回多个值时推荐用元组而不是列表?结合坐标 (x, y) 这种「位置固定、含义成对」的例子讲

课程思政|诚信与职业操守 · 一条数据要能对上人

元组常被拿来当字典的「组合键」,比如把 (姓名, 科目) 拧成一把钥匙,再去查对应的分数。这样查表又快又准,绝不会把两个同名同学的分数混在一起。数据必须能对上具体的人。统计里最怕的不是算错,而是张冠李戴——数字本身没错,却挂在了别人名下,那才叫失职。

十一、十个基础案例

下面十个例子都很短,建议每个都亲手敲一遍、跑一遍。

1. 创建一个「奶茶订单」元组(字符和数字混着放)

Python
t = ('奶茶', '三分糖', 12.5)
print(t)

输出:

输出
('奶茶', '三分糖', 12.5)

2. 取快递架上的最后一个取件码

Python
codes = ('A1', 'B2', 'C3', 'D4')
print(codes[-1])

输出:

输出
D4

3. 把课表倒过来看一眼

Python
t = ('语文', '数学', '英语', '体育')
print(t[::-1])

输出:

输出
('体育', '英语', '数学', '语文')

4. 把列表转成元组(锁住不让改)

Python
lst = ['奶茶', '炸鸡', '薯条']
t = tuple(lst)
print(t)

输出:

输出
('奶茶', '炸鸡', '薯条')

5. 用 + 拼接两份课表

Python
a = ('数学', '英语')
b = ('体育', '美术')
print(a + b)

输出:

输出
('数学', '英语', '体育', '美术')

6. 解包:把快递信息拆成变量

Python
info = ('菜鸟驿站', 'A-16')
place, code = info
print('地点', place, '取件码', code)

输出:

输出
地点 菜鸟驿站 取件码 A-16

7. 用 in 判断口味在不在菜单

Python
menu = ('原味', '芝士', '抹茶')
print('芝士' in menu)
print('榴莲' in menu)

输出:

输出
True
False

8. 数一数有几种小料

Python
t = ('珍珠', '椰果', '布丁', '仙草')
print(len(t))

输出:

输出
4

9. 用 for 遍历课表并带上序号

Python
t = ('周一', '周二', '周三', '周四')
for i, v in enumerate(t):
    print(i, v)

输出:

输出
0 周一
1 周二
2 周三
3 周四

10. 用(出发站, 终点站)当字典的键

Python
d = {('北京', '上海'): 553}
print(d[('北京', '上海')], '元')

输出:

输出
553 元
第 6 个案例的「解包」非常常用:函数返回多个值时,直接 a, b = func() 一次接住。解包这招在序列解包那篇还会专门讲。

十二、常见坑(含「元组里的列表可以改」)

坑 1:单元素元组忘了逗号

Python
a = (1)
print('a 其实是', type(a))

输出:

输出
a 其实是 <class 'int'>

想写只有一个元素的元组,必须 (1,),少了逗号就成了普通的整数。

坑 2:元组不能改,但「里面的列表」可以改

元组自己不可变,但如果某个元素是个列表,那个列表内部还能动:

Python
t = (1, [2, 3])
t[1].append(4)
print(t)

输出:

输出
(1, [2, 3, 4])

这是因为元组锁住的是「第 1 个元素是个列表」这件事,并没锁住那个列表里面的内容。所以别误以为元组里的一切都动不了。

坑 3:想用 append 往元组加东西

元组没有 append,写了会报 AttributeError(见报错手册)。要加东西,用 + 拼出新元组。

坑 4:以为 + 会改原元组

Python
t = (1, 2, 3)
t + (4,)
print('t 原样没变:', t)

输出:

输出
t 原样没变: (1, 2, 3)

t + (4,) 算出了一个新元组,但没赋值回 t,所以 t 还是老样子。要更新就写 t = t + (4,)。

坑 2 是最反直觉的:元组「不可变」只针对「直接装在里面的引用」。元素本身若是可变对象(列表、字典、集合),它们的内容照样能改。分不清时,记住「锁的是位置,不是内容」。
想往元组里加数据又必须保持元组形态?先 list() 转列表 → 增删改 → 再 tuple() 转回。这是处理「需要改但又想用元组」的标准流程。

十三、报错手册

下面这些报错都是本机真实运行出来的,照抄解释器原文。

改元素:TypeError

Python
t = (1, 2, 3)
t[1] = 10

运行后会报错(逐字照抄解释器):

报错
TypeError: 'tuple' object does not support item assignment

原因:元组不可变,不能给某个位置重新赋值。

删元素:TypeError

Python
t = (1, 2, 3)
del t[1]

运行后会报错(逐字照抄解释器):

报错
TypeError: 'tuple' object doesn't support item deletion

原因:同样因为不可变,连删除都不行。

用 append:AttributeError

Python
t = (1, 2, 3)
t.append(4)

运行后会报错(逐字照抄解释器):

报错
AttributeError: 'tuple' object has no attribute 'append'

原因:元组没有 append 这个方法(那是列表的)。要加元素用 t + (4,)。

索引越界:IndexError

Python
t = (1, 2, 3)
print(t[10])

运行后会报错(逐字照抄解释器):

报错
IndexError: tuple index out of range

原因:元组只有 3 个元素(索引 0~2),取第 10 个超出范围。

把非可迭代对象转元组:TypeError

Python
print(tuple(5))

运行后会报错(逐字照抄解释器):

报错
TypeError: 'int' object is not iterable

原因:tuple() 需要一个能一个一个取出来的东西(列表、字符串等)。数字 5 不是,会报「int 对象不可迭代」。

列表当字典键:TypeError

Python
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 题)

点击选项即可判分,选错的题会立刻展开解析。做完可以看到正确率和错题清单,也可以重新答题。

已答 0 / 12 · 正确 0 —

1元组在 Python 里通常用哪种括号表示?

  • A. []
  • B. ()
  • C. {}
  • D. <>
答案:B。元组用圆括号 () 表示,例如 (1, 2, 3)。方括号是列表,花括号是字典或集合。

2t = (1) 这个表达式里,t 的类型是什么?

  • A. int(整数)
  • B. tuple(元组)
  • C. str(字符串)
  • D. 会报错
答案:A。少一个逗号的 (1) 只是一层没用的括号,结果还是整数 1。type((1)) 得到 int,只有 (1,) 才是只有一个元素的元组。

3下面哪个操作会修改元组并导致报错?

  • A. 用 t[0] 读取第一个元素
  • B. 用 t[1:3] 切片
  • C. 用 for 遍历
  • D. 用 t[0] = 1 改第一个元素
答案:D。元组创建后不能改,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)
答案:B。元组本身不可变,但第 1 个元素是个列表,列表内部能改。所以 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)
答案:C。切片步长写 -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. 报错
答案:B。+ 不会改原元组,而是造出一个新元组 (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. 报错
答案:C。tuple() 把列表转成元组,结果是 (1, 2, 3)。反过来 list() 能把元组转回列表。

8函数 def f(): return 1, 2,调用 f() 得到的返回值类型是什么?

  • A. int
  • B. list
  • C. tuple
  • D. str
答案:C。Python 里函数「返回多个值」本质上是返回一个元组 (1, 2)。所以拿到后可以用 a, b = f() 一次性接住。

9要遍历元组 t 里的每个元素,正确的写法是?

  • A. for x in t:
  • B. for x in range(t):
  • C. while t:
  • D. 元组不能遍历
答案:A。元组和列表一样能用 for 元素 in 元组 遍历。range() 只收整数,不能写 range(t)。

10t = (1, 2, 3),想把它变成列表再修改,正确做法是?

  • A. t.append(4)
  • B. t[0] = 4
  • C. t.clear()
  • D. list(t) 然后改列表
答案:D。元组没有 append、不能下标赋值、也没有 clear。正确姿势是 lst = list(t) 转成列表改完,再 tuple(lst) 转回。

11元组 t = (5, 8, 5, 2),那么 t.count(5) 的结果是?

  • A. 2
  • B. 0
  • C. 1
  • D. 3
答案:A。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. 一个生成器(不是元组)
答案:D。Python 没有「元组推导式」。圆括号的推导式得到的是生成器,类型是 generator。要元组就写 tuple(x * x for x in range(3))。