跳到主要内容

4.3 字典 dict

本章的重头戏来了。列表解决了"存一串数据",但再看一个场景就会发现它的短板。

为什么需要字典

用列表存一个人的信息:

person = ["小明", 18, "上海"]

print(person[1]) # 18……这是年龄还是别的什么?

问题很明显:数据靠位置区分,位置没有含义person[1] 是年龄这件事,只存在你的脑子里——过一周你自己都忘了,别人读代码更是一头雾水。而且一旦中间插入一个新数据,后面所有位置全部错位。

我们想要的是:每个数据都带一个"名字",按名字存取。这就是字典(dict)

person = {"name": "小明", "age": 18, "city": "上海"}

print(person["age"]) # 18,指名道姓地取,谁看都明白

就像现实中的字典——通过"单词"查"释义",不用管这个词在第几页。

创建字典

花括号 {},每条数据是一对 键: 值,对与对之间用逗号隔开:

person = {"name": "小明", "age": 18, "city": "上海"}

拆开看这个结构:

person = { "name": "小明", "age": 18, "city": "上海" }
↑ ↑
键 : 值 ← 一对"键值对",冒号左键右值
(key) (value)
  • 键(key):数据的名字,通常用字符串。同一个字典里键不能重复(重复了后写的会覆盖先写的)
  • 值(value):数据本身,什么类型都行,可以重复

条目多的时候习惯竖着写,一行一对,更清晰(注意最后一行的逗号可留可不留,留着是 Python 社区习惯,方便以后增行):

person = {
"name": "小明",
"age": 18,
"city": "上海",
}
empty = {} # 空字典

取值:方括号和 get()

按键取值,语法和列表按索引取值长得像,只是方括号里放的是

print(person["name"]) # 小明
print(person["age"]) # 18

取一个不存在的键会报错:

print(person["phone"]) # ❌ KeyError: 'phone'

KeyError 意思是"没有这个键"。看到它,通常是两种情况:键名拼错了("Name""name",大小写敏感),或者这条数据真的没存过。

不确定键存不存在时,用更安全的 get() 方法:

print(person.get("phone")) # None(一个表示"空"的特殊值,不报错)
print(person.get("phone", "未填写")) # 未填写(第二个参数:查不到时的默认值)
print(person.get("name")) # 小明(查得到就正常返回)

两种取法的使用原则:这个键理应存在、缺了就是 bug → 用 [],让它报错帮你发现问题;键可能缺、缺了走默认逻辑 → 用 get() 处理用户输入、外部数据时多用 get()

增、改、删

字典和列表一样是可变的。增和改是同一个写法:

person = {"name": "小明", "age": 18}

person["age"] = 19 # 键已存在 → 改
person["city"] = "上海" # 键不存在 → 增
print(person) # {'name': '小明', 'age': 19, 'city': '上海'}

规则一句话:d[键] = 值,有这个键就是改,没有就是增。 不用像列表那样区分 append/insert,非常省心。

删除用 delpop()

del person["city"] # 直接删
age = person.pop("age") # 删掉并把值返回给你
print(age) # 19

删除不存在的键同样是 KeyError,删之前可以先用 in 判断:

print("name" in person) # True 注意:in 检查的是键,不是值!
print("小明" in person) # False "小明"是值,in 查不到值

遍历字典

字典配合 for 循环有三种遍历姿势,第一种最常用,务必记住

person = {"name": "小明", "age": 18, "city": "上海"}

# ① 同时遍历键和值:.items() —— 日常主力
for key, value in person.items():
print(f"{key}: {value}")
# name: 小明
# age: 18
# city: 上海

.items() 每轮产出一个 (键, 值) 元组,for key, value 正是上一节学的元组解包——知识串起来了。

# ② 只要键:直接遍历字典本身
for key in person:
print(key) # name age city

# ③ 只要值:.values()
for value in person.values():
print(value) # 小明 18 上海

列表套字典:真实世界的数据形态

一个字典存"一条记录",那"多条记录"怎么存?列表里装多个字典

students = [
{"name": "小明", "score": 90},
{"name": "小红", "score": 85},
{"name": "小刚", "score": 59},
]

处理套路:外层 for 遍历列表拿到每个字典,内层按键取值

for stu in students: # stu 依次是每个学生字典
if stu["score"] >= 60:
print(f"{stu['name']} 及格了")
else:
print(f"{stu['name']} 需要补考")

💡 细节:f-string 外层已经用了双引号,里面的键就用单引号 stu['name'],正是 2.3 节"内外引号错开"的规矩。

这个结构值得反复看几遍,因为它就是真实数据的样子——以后你调用网络接口拿到的 JSON 数据、读出来的 Excel 表格,几乎都是"列表套字典":列表的每个元素是一行记录,字典的每个键是一列字段。现在看懂它,以后学爬虫、数据分析就没有陌生感。

经典应用:用字典计数

统计一段文本里每个字符出现的次数——字典的招牌应用:

text = "hello world"
counter = {} # 空字典当计数板

for ch in text: # 逐个字符过一遍
if ch in counter: # 见过这个字符 → 次数 +1
counter[ch] += 1
else: # 第一次见 → 记为 1
counter[ch] = 1

print(counter)
# {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}

逻辑拆解:每个字符来了先问"计数板上有你吗?"——有就在原数字上加 1,没有就新开一行记 1。循环结束,计数板就是完整的统计结果。

为什么不能直接 counter[ch] += 1?因为第一次遇到 h 时,counter["h"] 还不存在,+= 1 等于"在不存在的数上加 1",直接 KeyError。所以必须先判断。(用 get() 有个漂亮的一行写法:counter[ch] = counter.get(ch, 0) + 1,思考一下为什么等价。)

这个"字典计数"套路是第 10 章词频统计项目的核心,现在多敲几遍血赚。

本节报错速查

报错人话翻译本节的触发场景
KeyError没有这个键d["不存在的键"] 取值或删除;键名拼错、大小写不对
TypeError类型不对用列表当键(键必须是不可变类型:字符串/数字/元组都行,列表不行)

练习

新建 practice43.py 完成:

1. 创建一个字典表示一部手机(品牌、价格、颜色),用 .items() 遍历打印成"品牌:华为"这样的格式。

2. 有字典 prices = {"苹果": 8, "香蕉": 5}:把香蕉价格改成 4,新增"橘子: 6",删除苹果,打印结果。

3. 不运行,先猜输出,再验证:

d = {"a": 1, "b": 2}
d["a"] = 100
d["c"] = 3
print(len(d))
print("b" in d)
print(2 in d)
print(d.get("x", 0))

4. 下面的程序想统计各水果出现次数,但一运行就报 KeyError,找出原因修好它:

fruits = ["苹果", "香蕉", "苹果", "橘子", "苹果"]
counter = {}
for f in fruits:
counter[f] += 1
print(counter)

5.(挑战)让用户连续输入 3 个朋友的名字和电话存进字典,最后按名字查询电话,查无此人时输出"查无此人"。

点击查看答案
# 1
phone = {"品牌": "华为", "价格": 4999, "颜色": "黑色"}
for k, v in phone.items():
print(f"{k}{v}")

# 2
prices = {"苹果": 8, "香蕉": 5}
prices["香蕉"] = 4
prices["橘子"] = 6
del prices["苹果"]
print(prices) # {'香蕉': 4, '橘子': 6}

第 3 题:

  • len(d)3(改 a 不增条目,加 c 增一条:a、b、c)
  • "b" in dTrue
  • 2 in dFalse(in 查的是键,2 是值)
  • d.get("x", 0)0(没有 x,返回默认值)

第 4 题: 第一次遇到"苹果"时 counter["苹果"] 还不存在,不能对不存在的键 += 1。先判断(或用 get):

fruits = ["苹果", "香蕉", "苹果", "橘子", "苹果"]
counter = {}
for f in fruits:
if f in counter:
counter[f] += 1
else:
counter[f] = 1
print(counter) # {'苹果': 3, '香蕉': 1, '橘子': 1}

# 5
contacts = {}
for i in range(3):
name = input("名字:")
tel = input("电话:")
contacts[name] = tel

query = input("要查谁的电话:")
print(contacts.get(query, "查无此人"))

本章小结

  • 字典 {键: 值} 按名字存取数据;键不能重复且必须是不可变类型,值随意
  • 取值:d[k] 严格(缺键 KeyError),d.get(k, 默认值) 宽容——按"缺了算不算 bug"来选
  • d[k] = v 有键则改、无键则增del d[k] / d.pop(k) 删除;in 判断的是
  • 遍历主力:for k, v in d.items()(底层是元组解包)
  • 列表套字典 = 真实数据的通用形态字典计数是必会套路(先判断再 +1)

下一节:4.4 集合 set