元组和集合

你已经了解了列表。Python 还有两种更多的集合类型,可以解决列表无法解决的问题。元组保存一组固定的值,这些值永远不会改变。集合只保持唯一值,让你可以瞬间检查成员资格,无论集合有多大。
元组
元组是一组有序的值,在创建后无法改变。圆括号定义一个元组,但它们是可选的。逗号才是真正使其成为元组的。单项元组需要尾随逗号。
point = (10, 20)
rgb = (255, 128, 0)
dimensions = (1920, 1080)
single = (42,) # 单项元组需要尾随逗号
also_tuple = 42, 99 # 括号是可选的;逗号使其成为元组按索引访问的工作方式完全与列表相同。尝试改变一个项目会引发 TypeError:
point = (10, 20)
point[0] # 10
point[1] # 20
point[-1] # 20
point[0] = 99 # TypeError: 'tuple' object does not support item assignment(42,) 需要那个孤独的尾随逗号。尝试重新赋值一个项目,你会得到 TypeError,这种被锁定的感觉正是你选择元组的原因。 何时使用元组
当你有一小组相关的值属于一起且不会改变时,使用元组。坐标 (x, y)、颜色 (r, g, b)、名称-分数对 ("张三", 87)。固定的结构向阅读代码的任何人表示这个组被视为单个单元。
(r, g, b) 颜色、名称和分数对。因为元组是可哈希的,你甚至可以将一个用作字典键。列表不能做到这一点,这在人们第一次尝试时会绊倒他们。 locations = {}
locations[(40, -74)] = "北京" # 元组作为字典键,可行
locations[[40, -74]] = "北京" # 列表作为字典键,TypeError解包
解包从元组中提取值,并在单行中将每个分配给自己的名称。名称数必须与值数匹配。使用 * 将任何剩余项目捕获到列表中。
x, y = point。名称数必须与值数匹配,除非你添加 *rest 来舀起剩余的。这对我来说在我停止到处写 point[0] 和 point[1] 的那一刻点了灯。 point = (10, 20)
x, y = point
print(x) # 10
print(y) # 20
first, *rest = [1, 2, 3, 4, 5]
# first = 1, rest = [2, 3, 4, 5]
head, *middle, tail = [1, 2, 3, 4, 5]
# head = 1, middle = [2, 3, 4], tail = 5命名元组
命名元组是每个位置都有名称的元组。你不用记住 point[0] 是 x 坐标,而是写 point.x。值仍然是不可变的;你得到可读的属性名而不是数字位置。
point.x 而不是记住 point[0] 是 x。它仍然完全不可变,在其他方面的表现就像常规元组。阅读代码的你的未来自我会为这些名称感谢你。 namedtuple 在标准库中,所以它首先需要一个导入:from collections import namedtuple。导入在模块章节中得到充分处理。
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
Player = namedtuple("Player", ["name", "score", "level"])
p = Point(10, 20)
p.x # 10
p.y # 20
alice = Player("张三", 87, 5)
alice.name # "张三"
alice.score # 87集合
集合是一个独特值的集合,没有保证的顺序。两次添加相同的值什么都不做:集合只保留每个项目的一个副本。使用花括号表示带项目的集合,或使用 set() 创建空集合。
set(),因为 {} 秘密地是空字典。最后这一点几乎会绊倒每个人。 tags = {"python", "beginner", "tutorial"}
numbers = {1, 2, 3, 4, 5}
empty = set() # 不是 {} (那是空字典)两次添加相同的值不会改变集合:
tags.add("python") # tags 未改变,"python" 已经在其中何时使用集合
集合是三件事的正确工具:从列表中移除重复项、快速检查某个东西是否在大型集合中、以及比较两个组以找到它们共享或不同的东西。
# 从列表中移除重复项
raw = ["cat", "dog", "cat", "bird", "dog", "cat"]
unique = list(set(raw)) # ["cat", "dog", "bird"] (顺序不保证)# 快速成员资格检查
valid_codes = {"USD", "EUR", "GBP", "JPY"}
code = "EUR"
if code in valid_codes: # 即使有数千个代码,也是即时查找
print("Valid")集合操作
集合支持你在数学课中学过的相同操作:并集(两个集合中的任何东西)、交集(两个集合共享的)和差集(一个有但另一个没有的)。Python 为这些使用运算符符号,每个都有方法等价物。
| 是并集(在两个之中),& 是交集(在两个之中),- 是差集(在一个但不在另一个),^ 是对称差集(在一个但不在两个之中)。每个还有一个拼写的方法如 .union(),如果你偏好词而不是符号。 a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # {1, 2, 3, 4, 5, 6} (并集:两个中的任何东西)
a & b # {3, 4} (交集:仅在两个中)
a - b # {1, 2} (差集:在 a 中但不在 b 中)
b - a # {5, 6} (差集另一种方式)
a ^ b # {1, 2, 5, 6} (对称差集:在一个但不在两个中)这些也有方法形式:.union()、.intersection()、.difference()、.symmetric_difference()。
修改集合
集合是可变的。.add() 添加一个项目。.update() 从任何列表或其他可迭代对象一次添加多个。.remove() 删除一个项目,但如果它不在那里则引发错误。.discard() 如果项目存在则静默删除,如果不存在则什么也不做。
.add() 放入一个项目,.update() 从列表或其他可迭代对象添加一整批。要分清的对是移除:.remove() 如果项目不在那里则出错,而 .discard() 耸肩继续。当你不确定它是否在集合中时,.discard() 为你节省一个流浪的错误。 tags = {"python", "beginner"}
tags.add("tutorial") # 添加一个项目
tags.update(["web", "api"]) # 从任何可迭代对象添加多个项目
tags.remove("beginner") # 移除,如果未找到则引发 KeyError
tags.discard("missing") # 移除,如果未找到则无错误
tags.pop() # 移除并返回一个任意项目
tags.clear() # 移除一切当你不确定项目是否存在时使用 .discard()。
冻结集合
冻结集合是创建后无法修改的集合。主要使用原因:冻结集合是可哈希的,所以它们可以用作字典键或存储在其他集合中。
valid_statuses = frozenset({"active", "paused", "deleted"})
valid_statuses.add("archived") # AttributeError, frozenset 是不可变的选择正确的集合
四种类型,每种有清晰的角色。问你需要对数据做什么,正确的选择通常就跟着来。
| list | tuple | set | dict | |
|---|---|---|---|---|
| 有序 | 是 | 是 | 否 | 是(插入顺序) |
| 可变 | 是 | 否 | 是 | 是 |
| 重复 | 是 | 是 | 否 | 否(键) |
| 访问方式 | 索引 | 索引 | 不适用 | 键 |
| 使用情况 | 有序、可改变的序列 | 固定记录 | 唯一值、快速成员资格 | 键值查找 |
一个快速决策规则:
- 需要按名称查找东西? → 字典
- 需要一个你将修改的有序集合? → 列表
- 有一个固定的相关值组? → 元组
- 需要唯一值或快速成员资格测试? → 集合
实际应用
使用元组存储固定记录,使用集合跟踪唯一值:
home = (39.9042, 116.4074) # 纬度,经度
office = (39.9015, 116.4073)
home_lat, home_lon = home
print(f"Home: {home_lat}, {home_lon}")
# 使用集合跟踪唯一访问者
visitors = set()
visitors.add("alice")
visitors.add("bob")
visitors.add("alice") # 已在集合中,静默忽略
visitors.add("carol")
print(f"Unique visitors: {len(visitors)}")
print(f"alice visited: {'alice' in visitors}")
print(f"dave visited: {'dave' in visitors}")
