Python 元组、集合与不可变性
AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。
列表和字典用得最多,但另外两种容器能省下很多代码:元组表达「固定结构」,集合解决「去重与归属判断」。
环境准备
示例在 Python 3.13 下验证,直接在 REPL 里试即可。
元组:固定结构的小记录
point = (3, 4)
x, y = point # 解包
print(x, y) # 3 4
括号常常可以省略,真正定义元组的是逗号:
a = 1, 2 # (1, 2)
single = (1,) # 单元素必须带逗号
not_a_tuple = (1) # 这是整数 1,不是元组
打包与解包
name, age = "小明", 18
first, *rest = [1, 2, 3, 4] # first=1, rest=[2, 3, 4]
a, b = b, a # 交换变量,不需要临时变量
函数返回多个值,本质就是返回一个元组:
def min_max(values):
return min(values), max(values)
low, high = min_max([3, 1, 9])
遍历字典的 items() 拿到的也是元组:
for key, value in {"a": 1, "b": 2}.items():
print(key, value)
具名元组
下标 point[0] 可读性差,用 NamedTuple 给字段起名:
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
p = Point(3, 4)
p.x # 3
p._asdict() # {'x': 3, 'y': 4}
它仍是元组:可解包、可比较、可当字典键。
不可变性到底是什么意思
元组本身不能增删元素,但里面的可变对象仍能改:
record = (1, [2, 3])
record[1].append(4) # 可以:改的是列表
record[1] = [2, 3, 4] # TypeError:不能换掉元素
可哈希性(能不能当字典键)取决于所有元素都可哈希:
{(1, 2): "ok"} # 可以
{(1, [2]): "ok"} # TypeError: unhashable type: 'list'
集合:去重与归属判断
tags = {"python", "web", "python"}
print(tags) # {'python', 'web'},重复自动去掉
集合无序,只能迭代不能按下标取。它的价值在 O(1) 的成员判断:
if "python" in tags: # 列表是 O(n),集合平均 O(1)
print("已存在")
增删:
tags.add("cli")
tags.discard("web") # 不存在也不报错
tags.remove("web") # 不存在会抛 KeyError
集合运算
a = {"python", "go"}
b = {"go", "rust"}
a | b # {'python', 'go', 'rust'},并集
a & b # {'go'},交集
a - b # {'python'},差集
a ^ b # {'python', 'rust'},对称差集(只在一边出现)
去重并保持顺序(集合会乱序):
items = ["b", "a", "b", "c"]
list(dict.fromkeys(items)) # ['b', 'a', 'c'],3.7+ 字典保序
需要不可变集合用 frozenset:
frozen = frozenset(["a", "b"])
{frozen: "可当键"} # 可以
四种容器怎么选
| 需求 | 选择 | 原因 |
|---|---|---|
| 有序、可改、允许重复 | list |
按下标访问、追加 |
| 固定结构、当字典键 | tuple / NamedTuple |
不可变、可哈希 |
| 去重、判断「在不在」 | set |
成员判断 O(1) |
| 键值映射 | dict |
按 key 取值 |
常见错误与注意事项
1. 忘了单元素元组的逗号
("a") 是字符串,("a",) 才是元组。
2. 依赖集合顺序
集合无序,set(["c", "a"])[0] 不可靠,也不要假设迭代顺序。
3. 把列表放进集合或字典键
{["a"]: 1} # TypeError: unhashable type: 'list'
改用元组或 frozenset。
4. 空集合的写法
{} # 这是空字典
set() # 这才是空集合
5. 用元组做「可变默认值」
元组本身不可变,但里面放列表照样会中「可变默认值」的坑(见函数那一篇)。
小结
- 元组表达固定结构,靠逗号定义,支持解包,可做字典键。
- 不可变是「元素引用不能换」,不代表内部对象不能改。
- 集合用于去重与快速成员判断,支持交并差运算,但无序。
- 选容器先看需求:有序可变用列表,映射用字典,去重归属用集合。