(十)字典:按名称查找和统计
1. 把查询条件放到键的位置
列表适合按位置保存一组值。要根据书名查询分钟数,可以把书名作为字典的键,分钟数作为对应的值:
# 花括号里的每项由“键: 值”组成。
reading = {"Python": 45, "NumPy": 30}
print(reading["Python"])
# 已有键会更新,新键会插入。
reading["Python"] = 50
reading["Pygame"] = 20
print(reading)
# get 查找键,没有找到时返回指定的默认值,不插入新记录。
print(reading.get("Django", 0))
print("Django" in reading)同一个键在字典里对应一个值。字典按插入顺序迭代,更新已有键不改变它的位置。若删除一个键后再次插入,它会进入新的末尾位置。
键需要能散列,也就是能够用于哈希查找。字符串、整数和由可哈希元素组成的元组都可以作为键;列表的内容可变,不能直接作为字典键。值则可以保存列表等对象,例如用书名对应一周的时长列表。
2. 遍历、删除与视图
# items 每轮提供一个“键、值”对,左边两个名称把它拆开。
reading = {"Python": 45, "NumPy": 30}
for title, minutes in reading.items():
print(title, minutes)
# keys 和 values 提供键视图和值视图,会反映字典后续的变化。
keys = reading.keys()
reading["Pygame"] = 20
print(list(keys))
# pop 按键删除并返回原来的值,第二个参数处理未找到的结果。
removed = reading.pop("Django", None)
print(removed)for title in reading 默认遍历键;for value in reading.values() 遍历值。准备在遍历时删改键的数量,可以先用 list(reading) 取一份键列表,再根据这份列表操作字典。这样遍历使用的数据和被修改的键集合分开了。
3. 按每条输入更新计数
# 为每个书名累计出现次数,缺少记录时从 0 开始。
titles = ["Python", "NumPy", "Python", "Pygame", "Python"]
counts = {}
for title in titles:
counts[title] = counts.get(title, 0) + 1
print(counts)输出 {'Python': 3, 'NumPy': 1, 'Pygame': 1}。每轮只改当前书名对应的一项。
第一次读到 Python,get 返回默认值 0,写入 1;再次读到同名书时取出旧计数,再加一。用默认值统一处理首次出现与后续出现,就不必为每次统计另写两套更新代码。
4. 缺省记录与映射合并
前面的 get 只读取已有记录;希望“没有这一项就先插入,然后返回它”,可以用 setdefault:
# 第一次查找插入空列表;第二次查找返回同一份已保存的列表。
groups = {}
first = groups.setdefault("小林", [])
first.append("Python")
second = groups.setdefault("小林", [])
second.append("NumPy")
print(first is second)
print(groups)输出 True 和 {'小林': ['Python', 'NumPy']}。第二次传入的空列表没有替换已有记录;返回的是该键当前对应的列表。第九章的 links.setdefault(link, None) 就利用同样的动作,为尚未见过的链接保存一项记录。
两份配置合并时,字典的 | 建立一个新字典,重复键采用右边的值;update 则把新项写入原字典:
# 右边覆盖重复的 goal,左边的 theme 继续保留。
base = {"goal": 30, "theme": "light"}
changes = {"goal": 45}
merged = base | changes
print(base)
print(merged)
# update 修改 base 本身,调用结果为 None。
result = base.update(changes)
print(base, result)先输出原配置,再输出 {'goal': 45, 'theme': 'light'};最后 base 也得到这份配置,result 为 None。新字典中的值仍按前面讲过的引用关系保存,合并动作不会递归复制内部列表。字典操作