讲解
元组(tuple)是不可变的有序序列,用圆括号创建:point = (3, 4)。创建之后不能增删改元素,除此之外下标、切片、len、in、count、index 的用法和列表完全一致。注意一个坑:单元素元组必须带逗号,(1) 只是加了括号的数字 1,(1,) 才是元组;其实创建元组靠的从来不是圆括号而是逗号——t = 1, 2, 3 同样是元组。
「不可变」带来元组的两大用武之地。其一是做字典的键和集合的元素:只有不可变(可哈希)的对象才能当键,列表不行,元组可以——比如用 (行, 列) 坐标做棋盘字典的键。其二是表达「结构固定的记录」:rgb 颜色 (255, 200, 100)、经纬度 (39.9, 116.4),用元组表示比列表更能传达「这个结构不会变」的意图。函数返回多个值时,返回的其实就是元组。
元组解包是 Python 最优雅的语法之一:x, y = (3, 4) 把元素分别赋给多个变量;交换变量 x, y = y, x 本质是右边构造元组、左边解包;遍历时 for name, score in [('小明', 90), ('小红', 85)]: 直接解开每一对。星号解包处理「剩余部分」:first, *rest = [1, 2, 3, 4] 让 first 拿 1、rest 拿 [2, 3, 4]。如果字段多了嫌下标语义不清,标准库 collections.namedtuple 能给元组的字段起名字,是轻量级的「小类」。
示例
# 创建与单元素陷阱
point = (3, 4)
not_tuple = (1)
real_tuple = (1,)
print(type(not_tuple), type(real_tuple))
print('逗号才是本体:', type((2,)))
# 下标切片与列表一致
rgb = (255, 200, 100)
print('红色分量:', rgb[0], ',切片:', rgb[:2], ',长度:', len(rgb))
# 不可变:rgb[0] = 0 会抛 TypeError
# 元组可以当字典的键,列表不行
board = {(0, 0): '车', (0, 1): '马', (7, 4): '将'}
print('棋盘原点:', board[(0, 0)])
# 函数返回多值 = 返回元组
def min_max(nums):
return min(nums), max(nums)
result = min_max([3, 1, 4, 1, 5])
print('返回的是:', result, type(result))
lo, hi = min_max([3, 1, 4, 1, 5]) # 解包接收
print('最小', lo, '最大', hi)
# 星号解包:吃剩余部分
first, *middle, last = [1, 2, 3, 4, 5]
print('首', first, '中', middle, '尾', last)
# namedtuple:给字段起名字
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print('按名字访问:', p.x, p.y, ',按下标也行:', p[0])
常见坑
- 单元素忘加逗号:t = (1) 是 int 不是 tuple,后面 t[0] 报错才发觉。单元素元组写 (1,)。
- 以为元组「深度不可变」:t = ([1, 2], 3) 的 t[0].append(9) 是合法的——元组锁住的只是「哪个槽位指向哪个对象」,槽位里的列表本身还能改。
- 硬用元组存该用列表的数据:元素个数会变、需要增删的场景用列表;元组适合字段固定的记录。混用会让代码到处是拼接新元组的笨拙操作。
- 解包个数不匹配:x, y = (1, 2, 3) 抛 ValueError。个数不确定时用星号 a, *rest = ... 兜住剩余。
小结
元组不可变、可哈希,能当字典键;逗号创建元组,单元素要带逗号;解包让多返回值和交换优雅;字段命名用 namedtuple。下一节看键值对结构:字典。