字典
列表让你按位置查找东西。但很多时候你想按名字查找,不是"给我第 3 项",而是"给我 Alice 的分数"。字典把数据存成键值对:你按键查值,而不是按位置查值。
创建字典
用花括号包起来,每个键和值之间用冒号分隔,各个键值对之间用逗号分隔。键几乎总是字符串。值可以是任何东西:数字、字符串、其他列表,甚至其他字典。
player = {
"name": "小明",
"score": 87,
"level": 5,
"alive": True,
}访问值
用方括号加键就能取到值。如果键不存在,Python 会抛出 KeyError。当你不确定某个键是否存在时,用 .get():它会返回 None 而不是报错崩溃,或者返回你指定的默认值。
player = {"name": "小明", "score": 87}
player["name"] # "小明"
player["score"] # 87
player["lives"] # KeyError(键不存在)player.get("score") # 87
player.get("lives") # None(不报错,默认返回 None)
player.get("lives", 3) # 3 (键不存在时使用这个默认值)只要键可能缺失,用 .get() 就更安全:
count = inventory.get("arrows", 0) # 如果字典里没有 "arrows" 就返回 0d["key"] 会把值给你,但如果那个键不存在就会抛出 KeyError。不确定的时候,用 .get():它会返回 None 而不是报错崩溃,或者返回你传入的默认值。我现在什么都默认加一下,程序就不再因为缺键而崩溃了。 添加与更新
用方括号给某个键赋值。如果这个键已经存在,值会被替换。如果还不存在,就会创建一个新条目。用 .update() 可以一次性把另一个完整的字典合并进来。
player = {"name": "小明", "score": 87}
player["score"] = 92 # 更新已有的键
player["level"] = 5 # 添加新键extras = {"level": 5, "alive": True}
player.update(extras) # 用 extras 里的键添加或覆盖.update() 可以把另一个完整的字典一次性并进来。 删除条目
有四种方式删除条目。.pop() 删除一个键并把值返回给你。带默认值的 .pop() 在键可能不存在时是安全的。del 删除一个键但没有返回值。.clear() 清空整个字典。
player = {"name": "小明", "score": 87, "level": 5}
player.pop("level") # 删除 "level" 并返回 5
player.pop("lives", None) # 安全删除,键不存在时返回 None
del player["score"] # 删除 "score",没有返回值
player.clear() # 删除所有内容带默认值的 .pop() 是删除一个可能不存在的键最安全的方式。
.pop(key) 删除一个键并把值交还给你,.pop(key, None) 在键可能不存在时也不会慌张。del d[key] 删除但不返回任何东西,.clear() 把一切清空。.pop() 的那个默认值参数帮我避开了很多 KeyError 的惊吓。 遍历
有三种"视图"让你遍历字典的不同部分。直接遍历字典得到的是键。.values() 得到值。.items() 一次性得到两者,也是你用得最多的方式:把每一对拆到两个名字里,循环就清晰易读。
player = {"name": "小明", "score": 87, "level": 5}
for key in player: # 遍历键(最常见)
print(key)
for key in player.keys(): # 一样,明确用键视图
print(key)
for value in player.values(): # 遍历值
print(value)
for key, value in player.items(): # 两者都要,最常用
print(f"{key}: {value}").items() 是你用得最多的方式。把每一对拆到两个名字里,能让循环更易读。
.values() 给你值,.items() 一次给两者,这也是你最常用到的一种。像 for key, value in player.items() 这样把每一对拆到两个名字里,让我的循环好读了不少。 检查成员
in 检查某个键是否存在于字典中。它只检查键,不检查值。要检查某个东西不存在,用 not in。
player = {"name": "小明", "score": 87}
"name" in player # True
"lives" in player # False
"lives" not in player # Truein 只检查键。要检查值,用 in player.values(),不过这种情况很少用到。
in 告诉你某个键是否在字典里,只看键,从不看值。不管字典有多大,速度都不会变慢。加个 not in 就能反过来检查某个东西是不是不存在。 嵌套字典
值本身也可以是字典。这就是表示多层结构化数据的方式:一个玩家有一段统计信息,一个配置文件有若干子部分。用两层方括号来访问嵌套值:第一层选外层的键,第二层选内层的键。
users = {
"小明": {"score": 87, "level": 5},
"小红": {"score": 74, "level": 3},
}
users["小明"]["score"] # 87
users["小红"]["level"] # 3用连续的方括号来访问。对于嵌套很深的结构,这会变得笨重,所以尽量让嵌套保持浅一些。
setdefault
.setdefault() 如果键存在就读取它,如果不存在就设为默认值,然后把值返回给你。当你需要确保某个键存在、但又不想覆盖它已有的值时,这个方法很好用。
inventory = {}
inventory.setdefault("arrows", 0) # 设置 "arrows": 0,返回 0
inventory.setdefault("arrows", 10) # "arrows" 已经存在,不改动,返回 0它对构建分组结构很有用,不需要先检查键是否存在:
groups = {}
for name, team in players:
groups.setdefault(team, []).append(name).setdefault(key, default) 如果键存在就读它,如果不存在就设成默认值并返回。这是确保一个键存在、又不破坏已有值的干净写法。我最常拿它来做分组,不用先判断"这个键在不在"。 collections.defaultdict 和 Counter
标准库里有两个字典子类,能自动处理常见的模式。defaultdict 会为缺失的键创建一个默认值,所以你永远不会遇到 KeyError。Counter 统计序列中每个元素出现的次数,并把结果以字典的形式给你。
defaultdict 和 Counter 存在于标准库中,所以要先导入才能用。导入的完整讲解在模块一章。
from collections import defaultdict
groups = defaultdict(list)
for name, team in players:
groups[team].append(name) # 即便 team 是新的也不会抛 KeyErrorfrom collections import Counter
words = ["猫", "狗", "猫", "鸟", "猫", "狗"]
counts = Counter(words)
# Counter({'猫': 3, '狗': 2, '鸟': 1})
counts.most_common(2) # [('猫', 3), ('狗', 2)]Counter 省掉了大量"用循环数东西"的重复代码。
defaultdict 会为任何缺失的键补上一个默认值,这样分组和计数就不会再朝你抛 KeyError。Counter 统计序列里每个元素出现的次数,并以字典的形式还给你。第一次把手写的计数循环换成 Counter 时,我的代码一下少了一半。 实战演练
构建一个分数追踪器,并打印包含所有条目的摘要:
scores = {"小明": 87, "小红": 74, "小刚": 92, "小丽": 55}
total = sum(scores.values())
average = total / len(scores)
print(f"玩家数: {len(scores)}")
print(f"平均分: {average:.1f}")
print(f"最高分: {max(scores.values())}")
print(f"最低分: {min(scores.values())}")
print()
for name, score in scores.items():
print(f" {name}: {score}")
