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,非常省心。
删除用 del 或 pop():
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 d→True2 in d→False(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