跳到主要内容

使用 dict 和 set

字典(dict)通过“键”查找“值”,适合表示具名字段和索引数据;集合(set)只保存不重复的元素,适合去重、成员判断和集合关系运算。

创建字典​

student = {
"name": "小林",
"age": 18,
"score": 92,
}
empty_dict = {}

键必须是可哈希对象,最常用的是字符串和数字;同一个字典中键不能重复。Python 3.7+ 按插入顺序保留字典项目,但字典的主要用途仍是按键查找,不应把它当作列表按位置使用。

也可以用 dict() 或字典推导式创建:

student = dict(name="小林", age=18)
squares = {number: number * number for number in range(1, 4)}
print(squares) # {1: 1, 2: 4, 3: 9}

访问和修改​

使用方括号访问键:

student = {"name": "小林", "score": 92}

print(student["name"])
student["score"] = 95 # 修改已有键
student["city"] = "上海" # 添加新键

访问不存在的键会抛出 KeyError。不确定键是否存在时,使用 get():

print(student.get("phone")) # None
print(student.get("phone", "未填写")) # 未填写

get() 的默认值只在键不存在时使用;如果键存在但值是 None,仍会返回 None。

安全地创建或更新​

setdefault() 在键不存在时写入默认值,并返回最终值:

groups = {}
groups.setdefault("python", []).append("小林")
groups.setdefault("python", []).append("小周")
print(groups) # {'python': ['小林', '小周']}

批量合并字典可以使用 update():

base = {"theme": "light", "language": "zh"}
base.update({"theme": "dark", "font_size": 14})
print(base)

Python 3.9+ 还支持 | 合并和 |= 原地更新:

defaults = {"timeout": 10, "retries": 2}
custom = {"timeout": 30}
config = defaults | custom

右侧同名键会覆盖左侧值;嵌套字典不会自动深度合并。

删除字典项目​

data = {"name": "小林", "age": 18, "city": "上海"}

city = data.pop("city") # 删除并返回值
age = data.pop("age", None) # 键不存在时返回默认值
last_key, last_value = data.popitem() # 删除并返回最后插入的项目
data.clear() # 清空

del data[key] 也可以删除项目,但键不存在时会抛出 KeyError。遍历字典时不要直接改变其大小:

data = {"a": 1, "b": 2, "c": 3}
for key in list(data):
if data[key] < 3:
del data[key]

遍历字典​

student = {"name": "小林", "score": 95}

for key in student: # 默认遍历键
print(key)

for value in student.values():
print(value)

for key, value in student.items():
print(f"{key}:{value}")

判断键是否存在使用 in:

if "score" in student:
print(student["score"])

key in dictionary 检查键,不检查值;检查值应使用 value in dictionary.values()。

集合基础​

集合使用大括号(至少有一个元素)或 set() 创建:

tags = {"python", "beginner", "python"}
print(tags) # 重复的 python 只保留一个

empty_set = set()
empty_dict = {}

集合无按位置访问的接口,元素顺序也不应作为业务依据。集合元素必须可哈希,因此列表、字典等可变对象不能直接放入集合。

常用操作:

tags.add("tutorial")
tags.update(["code", "learn"])
tags.discard("unknown") # 不存在也不报错
tags.remove("code") # 不存在时抛出 KeyError

使用 in 判断成员通常比在列表中搜索更适合大量查找:

blocked = {"bot", "spammer"}
if username in blocked:
print("拒绝访问")

集合运算​

设有两个标签集合:

frontend = {"html", "css", "javascript"}
backend = {"python", "go", "javascript"}

print(frontend | backend) # 并集
print(frontend & backend) # 交集
print(frontend - backend) # 差集
print(frontend ^ backend) # 对称差集

对应的方法是 union()、intersection()、difference() 和 symmetric_difference()。还可以判断子集和超集:

required = {"html", "css"}
print(required <= frontend) # True,子集
print(frontend >= required) # True,超集

集合运算返回新集合;带 _update 后缀的方法(如 intersection_update())会原地修改集合。

字典和集合的可哈希键​

字典键和集合元素需要稳定的哈希值。整数、字符串和元素也可哈希的元组通常可以使用;列表、字典和集合不可哈希:

locations = {(31.2, 121.5): "上海"}
print(locations[(31.2, 121.5)])

# {[1, 2]} # TypeError:list 不可哈希
# {"key": []} # 这里列表作为值可以,作为键不行

元组能否作为键取决于其中所有元素是否可哈希:(1, "a") 可以,(1, [2]) 不可以。

计数和分组​

使用普通字典可以实现计数:

words = ["python", "go", "python", "dart"]
counts = {}

for word in words:
counts[word] = counts.get(word, 0) + 1

print(counts) # {'python': 2, 'go': 1, 'dart': 1}

标准库 collections.Counter 更适合计数:

from collections import Counter

counts = Counter(words)
print(counts.most_common(2))

分组时可以使用 setdefault(),或使用 collections.defaultdict:

from collections import defaultdict

groups = defaultdict(list)
for name, language in [("小林", "Python"), ("小周", "Python"), ("小王", "Go")]:
groups[language].append(name)

常见错误​

  • data["missing"] 和 data.get("missing") 的行为不同:前者报错,后者返回默认值。
  • set() 的输出顺序不固定,不要用 set 表示需要排序或按位置访问的数据。
  • 通过 dict.fromkeys(keys, []) 创建嵌套列表会共享同一个列表;需要独立列表时使用字典推导式。
  • 字典和集合的“键/元素”必须可哈希;需要以列表内容作为键时,可先转换为元组。
  • 浅复制字典不会复制嵌套的列表或字典,必要时使用 copy.deepcopy() 或明确复制内层数据。

综合示例:找出共同选修课程​

alice = {"Python", "数据库", "英语"}
bob = {"Go", "数据库", "英语"}

common = alice & bob
all_courses = alice | bob

print(f"共同课程:{'、'.join(sorted(common))}")
print(f"全部课程数量:{len(all_courses)}")

集合负责去重和关系运算,sorted() 只在展示时提供稳定顺序。

练习​

  1. 使用字典保存商品名称和库存,完成一次购买后更新库存,并处理商品不存在的情况。
  2. 统计一段文本中每个单词的出现次数,忽略大小写。
  3. 给定两个用户兴趣集合,求共同兴趣和只属于第一个用户的兴趣。
  4. 将学生按班级分组,输出每个班级的人数。
  5. 设计一个字典缓存函数结果,命中缓存时直接返回,未命中时计算并保存。

小结​

  • dict 通过键查找值,set 保存不重复元素;两者都适合快速成员判断。
  • get() 安全读取,setdefault() 可按需初始化,update() 和 | 可合并字典。
  • 字典遍历使用 keys()、values()、items();判断键存在直接使用 in。
  • 集合支持并集、交集、差集和对称差集,顺序不应作为集合数据的语义。
  • 字典键和集合元素必须可哈希;可变容器不能直接作为键或集合元素。