① 一句话定义:元组 = 有序 + 不可变 + 可哈希 📜
有序序列,元素不可增删改,整体可哈希(当键值)。
类比:模具 = 类,饼干 = 对象,元组 = 密封饼干袋(不能加饼干,但能当标签)。
② 创建语法:3 种写法一次记牢 📝
# 1. 小括号(推荐)
t = (1, 2, 3)
# 2. 无括号(Pythonic)
t = 1, 2, 3
# 3. 单元素逗号(必考)
single = (42,) # 不是 (42)
口诀:“单元素逗号,无括号也行,小括号最清晰!”
③ 核心操作:一张表速查 ⚡️
| 操作 | 代码 | 结果 |
|---|---|---|
| 索引 | t[0] | 1 |
| 切片 | t[::-1] | (3, 2, 1) |
| 长度 | len(t) | 3 |
| 拼接 | t + (4,) | (1, 2, 3, 4) |
| 重复 | (0,) * 3 | (0, 0, 0) |
| 统计 | t.count(2) | 1 |
| 查找 | t.index(3) | 2 |
没有
append/extend/remove!想改→先转list
④ 哈希钥匙:当 dict/set 的键 🗝️
# 合法:元组里全是不可变元素
loc = {(116.4, 39.9): "北京", (121.5, 31.2): "上海"}
# 非法:内含列表
# loc = {(1, [2, 3]): "home"} # TypeError: unhashable type 'list'
结论:“元组可哈希,但内部必须全是可哈希元素”
⑤ 拆包艺术:一行赋值多个变量 🎁
# 位置拆包
x, y, z = (1, 2, 3)
# 星号拆包
first, *middle, last = (1, 2, 3, 4, 5) # middle=[2,3,4]
# 函数多返回
def divmod(a, b): return a // b, a % b
q, r = divmod(10, 3) # q=3, r=1
口诀:“星号收中间,位置收两边,函数返回自动打包”
⑥ 性能对比:比 list 快 & 省内存 🚀
import sys, timeit
lst = [1, 2, 3, 4, 5]
tup = (1, 2, 3, 4, 5)
sys.getsizeof(lst) # 104 bytes
sys.getsizeof(tup) # 80 bytes ↓23%
timeit.timeit('x[2]', globals={'x': lst}) # 28 ns
timeit.timeit('x[2]', globals={'x': tup}) # 22 ns ↓21%
结论:迭代、索引、哈希都略快,大数据优先 tuple
⑦ 嵌套陷阱:内部可变元素可改 💣
# 元组本身不可变,但内部列表可以改
t = (1, [2, 3])
t[1].append(4) # OK → (1, [2, 3, 4])
t[0] = 9 # TypeError!不能改顶层
规避:使用
tuple存不可变数据,或dataclass冻结结构
⑧ 现代替代:@dataclass 冻结 🧊
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(1, 2)
p.x = 3 # dataclasses.FrozenInstanceError
结论:需要字段名 + 类型提示 → 用 frozen dataclass 代替裸 tuple
⑨ 趣味实战:元组深拷贝调试器(10 行)
import copy, json
def debug_copy(obj):
"""打印对象及其一层子对象的 id"""
print(f"Root id: {id(obj)}")
if hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)):
for i, item in enumerate(obj):
print(f" [{i}] id: {id(item)} -> {type(item)}")
# 使用
a = [1, [2, 3]]
b = copy.deepcopy(a)
debug_copy(a)
debug_copy(b)