← 返回题目列表

Python tuple 有什么特点?元组拆包有哪些常见用法?

高频 简单 第 6 / 21 题 更新于 2026/07/25
Pythontuple元组拆包

简化版

tuple 是有序、不可变的序列,常用于表达固定结构的数据,比如坐标、函数多返回值。元组拆包可以把序列中的元素一次性绑定到多个变量,还支持 * 收集剩余元素。

详细版

基本用法:

point = (10, 20)
x, y = point
print(x, y)  # 10 20

函数多返回值本质上常见就是返回 tuple:

def get_user():
    return 1, "Tom"

user_id, name = get_user()

扩展拆包:

nums = (1, 2, 3, 4, 5)
first, *middle, last = nums
print(first)   # 1
print(middle)  # [2, 3, 4]
print(last)    # 5

注意:

  • 单元素元组必须写逗号:(1,)
  • tuple 自身不可变,但内部引用的可变对象仍然可能被修改。
  • 只有所有元素都可哈希时,tuple 才能作为 dict key。

完整版教学

一、tuple 的不可变指的是什么

tuple 创建后,不能替换、增加或删除元素:

t = (1, 2, 3)
# t[0] = 100  # TypeError

但如果 tuple 里放的是可变对象,可变对象本身仍然可以改:

t = ([1, 2], "python")
t[0].append(3)
print(t)  # ([1, 2, 3], 'python')

这里 tuple 的第一个位置仍然引用同一个列表对象,没有替换 tuple 的元素;变化发生在列表内部。

可以这样理解:

t = ([1, 2], "python")

tuple 槽位 0 ──> list 对象 [1, 2]
tuple 槽位 1 ──> str 对象 "python"

槽位不能换,但槽位 0 指向的 list 自己可以 append

所以“tuple 不可变”不是深度不可变。它保证的是 tuple 自身的长度和每个位置的引用不变,不保证内部引用对象的内容不变。

二、为什么单元素元组要加逗号

这点很高频:

a = (1)
b = (1,)

print(type(a))  # <class 'int'>
print(type(b))  # <class 'tuple'>

括号本身不决定 tuple,逗号才是关键。(1) 只是给表达式加括号,(1,) 才是单元素 tuple。

甚至不写括号也可以创建 tuple:

t = 1, 2, 3
print(type(t))  # <class 'tuple'>

括号更多是为了分组和可读性;真正让表达式成为 tuple 的是逗号。这也是函数多返回值的基础:return a, b 实际返回的是一个 tuple。

三、元组拆包让代码更清晰

交换变量:

a, b = b, a

遍历键值对:

user = {"name": "Tom", "age": 18}
for key, value in user.items():
    print(key, value)

函数返回多个值:

def divide(a, b):
    return a // b, a % b

quotient, remainder = divide(10, 3)

这些写法背后都利用了序列拆包。

拆包要求左边变量数量和右边元素数量匹配,否则会报 ValueError。这能帮助你尽早发现数据结构不符合预期,而不是悄悄丢字段。比如处理 (status, data) 这种固定结构时,拆包比 result[0]result[1] 更表达语义。

pair = (200, "ok")
status, message = pair

四、星号拆包处理不定长度

当序列长度不固定时,可以用 * 接住一部分:

head, *tail = [1, 2, 3, 4]
print(head)  # 1
print(tail)  # [2, 3, 4]

也可以放中间:

first, *body, last = [1, 2, 3, 4]
print(body)  # [2, 3]

注意,被 * 接住的部分是 list,不是 tuple。

数字例子:first, *middle, last = (1, 2, 3, 4, 5) 中,first 得到 1,last 得到 5,中间剩下 3 个元素被收集成列表 [2, 3, 4]。一个拆包表达式里只能有一个带星号目标,否则 Python 不知道如何分配剩余元素。

记忆钩子:普通拆包要求数量精确匹配,星号拆包给“不确定长度的中间部分”留了一个收集口。

五、tuple 什么时候适合做 dict key

tuple 可以作为 dict key 的前提是它本身可哈希,而这要求内部元素也可哈希。

可以:

locations = {}
locations[(10, 20)] = "A"

不可以:

# d = {([1, 2], 3): "bad"}  # TypeError

因为列表不可哈希,包含列表的 tuple 也不可哈希。

常见用法是坐标、复合 key、多字段索引:

scores = {}
scores[("class-1", "Tom")] = 95
scores[("class-1", "Jerry")] = 88

这里 tuple 表示固定结构的两个字段,顺序有意义:("class-1", "Tom")("Tom", "class-1") 不是同一个 key。如果组合不关心顺序,用 frozenset 可能更合适。

六、tuple 和 list 怎么选

tuple 更适合固定结构,list 更适合同质、可变的一组元素。比如坐标 (x, y)、颜色 (r, g, b)、函数返回 (status, payload) 都是 tuple 的典型场景;用户列表、任务队列、可增删的结果集更适合 list。不要因为 tuple “性能可能更好一点”就到处用 tuple,语义优先。

场景更适合原因
坐标 (x, y)tuple固定字段结构
函数多返回值tuple天然可拆包
动态收集数据list需要增删改
dict 复合 keytuple不可变且可哈希条件更容易满足

七、常见误区与追问

  • 误区:(1) 是单元素 tuple。 这是带括号的整数表达式,单元素 tuple 必须写 (1,)
  • 误区:tuple 内部任何内容都不能变。 tuple 槽位不可变,但槽位指向的 list 等可变对象仍能变。
  • 误区:星号拆包接住的是 tuple。 带星号目标接住的是 list。
  • 追问:函数多返回值本质是什么? 常见情况下是返回一个 tuple,调用方再拆包。
  • 追问:tuple 什么时候不能做 dict key? 当内部包含 list、dict、set 等不可哈希对象时,整个 tuple 不可哈希。
  • 追问:tuple 和 list 怎么选? 固定结构、不可变语义、复合 key 用 tuple;动态增删的一组数据用 list。

八、加强记忆

把 tuple 记成“固定结构的有序记录”:逗号才是 tuple 的关键,单元素要写 (x,);拆包让固定结构的字段绑定更清楚,星号拆包处理不定长度但接住 list。tuple 是否能做 dict key 取决于内部元素是否都可哈希,内部可变对象仍可能被修改,这两个边界最容易被追问。