元组
约 1473 字大约 5 分钟
2025-04-04
元组是不可变的有序序列,可以当作“只读列表”理解。它真正的价值不在“少了几个方法”,而在不可变带来的安全性、可哈希性、以及作为函数多返回值的天然容器。
- 定义与单元素元组的逗号陷阱
- 索引、切片、count、index
- 元组解包与多返回值
- 可哈希:能做 dict 的 key
- namedtuple:让元组也能按名字访问
- 1分别写 (1)、(1,)、() 三种,用 type() 看类型 —— 单元素元组的逗号是必须的。
- 2用 a, b = (1, 2) 解包一个元组,再用 a, b = b, a 一行交换,体会“多重赋值”底层就是元组解包。
- 3写一个函数 return x, y,调用时直接 a, b = func(),理解“函数多返回值”就是返回了元组。
- 4把 (1, 2) 作为 dict 的 key 存入,再换成 [1, 2] —— 看到 TypeError,体会“可哈希必须不可变”。
- 5试 tu = (1, [2, 3]); tu[1].append(4) —— 元组本身不变,但里面装了可变对象就还会变。
元组的关键字是 tuple。
元组的定义方法和列表极其相似,只是将 中括号 变成了 小括号。很多时候,小括号可以省略:
tu = (1, 2, 3, 'alice')
tu1 = 1, 2, 3, 'alice'元组就是一个 不可变 的列表。因为不可变,元组没有增删改的方法,只能进行查看。
元组也可以通过索引方式进行查找,也同样支持切片操作:
python playground
tu = 1, 2, 3, 'alice'
print(tu[0]) # 1
print(tu[1:3]) # (2, 3)注意元组切片后得到的还是元组数据。
元组同样可以通过 for 循环的方法来查看:
python playground
tu = 1, 2, 3, 'alice'
for i in tu:
print(i)元组的 .count() 方法可以用来统计指定元素在元组中出现的次数;.index() 方法可以通过元素的名称获取元素的索引:
python playground
tu = 1, 2, 3, 'alice'
print(tu.count(3)) # 1
print(tu.index(3)) # 2需要注意的是,并不是出现小括号表示该数据的数据类型时元组。当小括号中没有出现逗号时,数据类型就是括号中数据类型本身:
python playground
a = (10)
print(type(a)) # <class 'int'>
b = ('alice')
print(type(b)) # <class 'str'>当小括号中没有数据时,代表的是空元组:
python playground
c = ()
print(c) # ()元组的应用场景有:
- 为了防止误操作时修改数据,元组用来存放一些重要数据
- 配置文件中用来存储数据
一定要掌握下面这三种小括号的含义:
tu = (10) # int,括号中只有一个元素且没有任何逗号,那就表示该元素本身。此时,括号可以看成一个运算符。
tu = (10,) # tuple,括号中虽然只有一个元素,但是元素后面有一个逗号,表示这是一个元组。
tu = () # tuple,这时空元组的表示方法,括号中什么都没有。元组解包:函数多返回值的秘密
元组最常见的实战场景不是“存一些不变的数据”,而是解包。当一个函数想返回多个值时,Python 的惯用做法是返回一个元组,调用方再解包:
python playground
def divmod_(a, b):
return a // b, a % b # 实际返回一个 (商, 余数) 元组
q, r = divmod_(17, 5) # 解包成两个变量
print(q, r) # 3 2a, b = b, a 这种一行交换的写法,本质也是元组解包:右边 b, a 先组成一个临时元组 (b_value, a_value),再被拆给左边。
需要忽略某些位置时,惯例上用下划线 _:
python playground
person = ('alice', 18, 'female')
name, _, gender = person # 不关心年龄
print(name, gender)数量不确定时,用星号 * 收集:
python playground
first, *middle, last = (1, 2, 3, 4, 5)
print(first, middle, last) # 1 [2, 3, 4] 5元组是“浅不可变”
一个常见的误解是:“元组不可变 = 里面的东西也改不了”。这只在元组里全是不可变元素时成立。如果元组里装了一个列表,列表本身还是可以改的:
python playground
tu = (1, 2, [3, 4])
tu[2].append(5) # 改的是列表,不是元组
print(tu) # (1, 2, [3, 4, 5])
# tu[2] = [99] # 这才会报错:TypeError: 'tuple' object does not support item assignment所以只有“全是不可变元素”的元组才可哈希,可以当 dict 的键或 set 的元素:
python playground
d = {(0, 0): 'origin', (1, 2): 'point'}
print(d[(1, 2)])
# 包含 list 的元组不可哈希
# d = {(1, [2, 3]): 'oops'} # TypeError: unhashable type: 'list'namedtuple:让元组按名字访问
普通元组的缺点是位置含义不直观:person[0] 是名字还是年龄?标准库 collections.namedtuple 给元组加上字段名,既保留不可变性,又能按名字读取:
python playground
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(p.x, p.y) # 3 4
print(p[0], p[1]) # 仍然支持下标
print(p) # Point(x=3, y=4)Python 3.7+ 还有更现代的 typing.NamedTuple 和 dataclasses.dataclass(frozen=True),但 namedtuple 因为轻量、可哈希、行为像元组,仍然是表示“固定结构小数据”的好工具。
- 元组是不可变序列,定义靠逗号不靠括号:(1,) 是元组,(1) 是整数。
- 解包是元组最高频的用法:a, b = func()、a, b = b, a,星号 * 收集剩余、下划线 _ 占位。
- “元组不可变”指的是元组本身的元素引用不能换;如果引用的是可变对象,对象内容仍可变。
- 全不可变的元组可哈希,能做 dict 的 key 或 set 的元素,列表则不行。
- 需要给固定结构的字段命名时,用 collections.namedtuple,比普通元组可读性高很多。
版权所有
版权归属:Shuo Liu
