第13章-字典


昨天学的列表,存"一串同类型的东西"很顺手,但它有个短板:找数据只能靠"第几个"。比如 ["小明", 18, "北京"],你要拿年龄,得先记住"年龄在第 1 个位置"。万一中间加了一项,全乱套。

真实世界的数据几乎都有名字:人有姓名、年龄、城市,商品有名字和价格,网站要存几百万用户的资料。存这种"有名字的数据",Python 给了专门的容器——字典。它就像一本通讯录:要找小明的电话,直接翻到"小明"那一页就行,不用从头数到第几个。

今天学完,你就能写出"成绩单"这类小程序:用字典存同学成绩,遍历打印"谁:多少分"。

字典长什么样:花括号里的键值对

字典用花括号 {} 包起来,里面是"键:值"的配对,用逗号隔开。(key)是"名字",(value)是"内容",一个键对应一个值,就像电话簿里一个名字对应一个号码:

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

#   键(key)     值(value)
#   name   →   小明
#   age    →   18
#   score  →   92

怎么读它?用键取值,就像翻通讯录:

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

print(student)              # {'name': '小明', 'age': 18, 'score': 92}
print(student["name"])      # 小明(用键取值,就像翻通讯录)
print(len(student))         # 3(有几个键值对)

empty = {}                  # 空字典,以后往里加

输出:

{'name': '小明', 'age': 18, 'score': 92}
小明
3

一句话记住结构:{"名字": 内容},左边是键(怎么称呼它),右边是值(它到底是什么)。键一般是字符串,值可以是任何类型。

几个容易写错的地方:字典用花括号 {},别和列表的方括号 [] 混了;键和值之间用冒号 :,键值对之间用逗号 ,——位置写错直接 SyntaxError;字典里的键不能重复,后写的会覆盖先写的(同一个名字只能有一页电话)。

查:从字典里取值

取字典里的值有两种写法,差别在于"键不存在时怎么办":

写法 键存在时 键不存在时
字典["键"] 返回值 报错 KeyError
字典.get("键") 返回值 返回 None不报错

日常开发推荐用 get():拿不到数据也不至于让程序崩溃,这是今天最重要的一个点。看代码:

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

print(student["name"])       # 小明(键存在,正常取)
# print(student["city"])     # 报错!KeyError: 'city'(键不存在)

print(student.get("name"))   # 小明
print(student.get("city"))   # None(不报错,返回"空")
print(student.get("city", "未知"))   # 未知(找不到时给个默认值)

get 的第二个参数就是兜底:键找不到时,返回你给的那个默认值:

student = {"name": "小明"}

name = student.get("name", "无名氏")
city = student.get("city", "未知城市")     # 没有 city 这个键
print(f"姓名:{name},城市:{city}")

输出:

姓名:小明,城市:未知城市

查之前想先确认键在不在,用 in——注意它判断的是,不是值:

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

if "name" in student:        # 判断的是"键"在不在,不是值!
    print("有 name 这个键")

if "city" not in student:
    print("没有 city 这个键")

输出:

有 name 这个键
没有 city 这个键

三个点说清楚:in 判断的是在不在——"小明" in student 是 False,因为小明是值不是键;get() 找不到返回 NoneNone 打印出来就是这个单词,别把它当成报错;真需要"键不存在就报错让程序停"的场合很少,所以默认记 get 就行。

增和改:其实是同一个动作

往字典里加键、改值,用的是同一个动作:直接给键赋值。键不存在就是"新增",键已存在就是"修改",Python 自动判断,你什么都不用管:

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

student["city"] = "北京"     # 没有 city 这个键 → 新增
print(student)              # {'name': '小明', 'age': 18, 'city': '北京'}

student["age"] = 19         # 有 age 这个键 → 修改
print(student)              # {'name': '小明', 'age': 19, 'city': '北京'}

"新增"和"修改"的写法完全一样,判断标准只有一条:这个键之前存不存在。想一次加多个键值对,用 update()

student = {"name": "小明"}

student["age"] = 18                    # 新增 age
student.update({"score": 92, "city": "北京"})   # 一次新增多个键
print(student)

student["age"] = 19                    # 修改 age
print(student["age"])                  # 19

输出:

{'name': '小明', 'age': 18, 'score': 92, 'city': '北京'}
19

别把增改想复杂了,它就是普通赋值:student["age"] = 19,没有别的魔法。

删:从字典里移除键

删键值对最常用 pop(键),它会把"键和值一起拿走",和列表的 pop() 有点像,但这里传的是

方法 作用 例子
pop("键") 删掉这个键,并返回它的值 student.pop("score")
del 字典["键"] 删掉这个键(不返回值) del student["score"]
student = {"name": "小明", "age": 18, "score": 92}

score = student.pop("score")    # 删掉 score 键,值 92 被接住
print(score)                   # 92
print(student)                 # {'name': '小明', 'age': 18}

del student["age"]             # 删掉 age 键
print(student)                 # {'name': '小明'}

删不存在的键会报 KeyError,所以稳妥的做法是先判断再删:

student = {"name": "小明"}

if "score" in student:          # 有才删,没有就算了
    student.pop("score")
else:
    print("没有 score 这个键,不用删")

输出:

没有 score 这个键,不用删

三点记牢:pop 的参数是不是值——student.pop("小明") 会报 KeyError(小明是值);删不存在的键,popdel 都会报错,先 in 判断最稳;想清空整个字典用 student.clear(),得到一个空字典 {}

遍历:把字典从头到尾过一遍

遍历列表是"每个元素来一遍",遍历字典是"每个键值对来一遍"。有三种方式,最常用的是 items()——一次拿到"键和值"两个东西:

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

# 方式一:只拿键
for key in student:
    print(key)                  # name / age / score

# 方式二:键 + 值(用键去字典里取)
for key in student:
    print(key, student[key])    # name 小明 / age 18 / score 92

# 方式三:最常用,用 items 一次拿两个
for key, value in student.items():
    print(f"{key}: {value}")

第三种方式最省事,写成绩单就用它:

scores = {"小明": 92, "小红": 85, "小刚": 78}

for name, score in scores.items():
    print(f"{name}{score} 分")

输出:

小明:92 分
小红:85 分
小刚:78 分

有时候只想拿键或只想拿值,用 keys()values()

scores = {"小明": 92, "小红": 85, "小刚": 78}

print(scores.keys())       # dict_keys(['小明', '小红', '小刚'])
print(scores.values())     # dict_values([92, 85, 78])

# 实际使用:把值取出来算平均分
all_scores = list(scores.values())     # 转成列表 [92, 85, 78]
print(sum(all_scores) / len(all_scores))   # 85.0

输出:

dict_keys(['小明', '小红', '小刚'])
dict_values([92, 85, 78])
85.0

三个坑提前讲:for key in student: 只拿到键,要值得写 student[key],想一步到位就用 items()items() 拿出来的循环变量是两个——for key, value in ...,漏一个会报 ValueError: not enough values to unpackkeys() / values() 返回的不是列表(是视图),要用 list() 包一下才能当列表用。

列表还是字典:什么时候用哪个

这是今天最重要的一道判断题。记一句话:存"一串同类型的东西"用列表,存"一个东西的多个属性"用字典

场景 用什么 例子
一串成绩 / 名单 / 商品名 列表 [92, 85, 78]
一个人的信息(姓名+年龄+电话) 字典 {"name": "小明", "age": 18}
全班同学的信息(一串人,每人多个属性) 列表套字典 [{"name": "小明", "score": 92}, ...]

第三行是重中之重:列表 + 字典组合(一个字典列表)几乎能表示所有业务数据,明天的课专门讲它,今天先记住结论:

# 什么时候用列表:一串同类型的
scores = [92, 85, 78]          # 一串分数,没有名字

# 什么时候用字典:一个东西的多个属性
student = {"name": "小明", "age": 18, "city": "北京"}   # 一个人的名片

# 怎么选:先问自己"数据有没有名字"——有名字用字典,没名字用列表

选择的标准其实就一句话:别拿"第几个"的习惯硬套字典——字典没有"第 0 个",只有"哪个键";反过来,成绩单如果不要名字、只要一串数,那列表就够了。先想清楚数据结构,再写代码。这两个容器都是本周必会,判断标准要能背出来,周测会考。

随堂练习

练习一:我的名片

建一个字典存你自己的信息(姓名、年龄、城市),用 get 取年龄打印,再新增一个"爱好"键,最后遍历打印全部键值对。

提示:me = {} 空字典开始;新增直接 me["爱好"] = "...";遍历用 for k, v in me.items():

练习二(必做):成绩单

用字典存 3 个同学的成绩({"小明": 92, "小红": 85, "小刚": 78}),用 for 遍历打印"谁:多少分",再用 values() 算出平均分。

提示:平均分 = sum(字典.values()) / len(字典)

练习三:判断键

in 判断"小丽"在不在成绩单里,不在就提醒"还没有小丽的成绩"。

进阶题(可选):带列表属性的字典

给字典加一个列表属性:{"name": "小明", "hobbies": ["篮球", "吉他"]},用 for 循环遍历打印出所有爱好(外层遍历字典拿到 hobbies,内层遍历 hobbies 列表)。

写不出来时,让 AI 搭把手

场景:成绩单小程序写不出来,需要 AI 给参考代码
提示词:
我是 Python 初学者,今天学字典。请帮我写一个成绩单小程序:
用字典存 3 个同学的成绩,遍历打印"谁:多少分",
再算出平均分。用 for k, v in 字典.items() 的方式遍历,代码加注释。
场景:不确定某个数据该用列表还是字典,请 AI 帮忙判断
提示词:
我在学 Python 数据结构,想存这样的数据:多个学生的姓名和成绩。
我应该用列表、字典,还是列表套字典?请用大白话解释三种方案的区别,
并各给一行代码示例。我是初学者,不要讲专业术语。

常见报错速查

报错信息 什么意思 怎么办
KeyError: 'xxx' 字典里没有这个键 改用 get() 取值,或先用 in 判断再取
ValueError: not enough values to unpack items() 遍历时循环变量写少了 写两个:for key, value in 字典.items():
TypeError: unhashable type: 'list' 拿列表当键了 键只能是字符串/数字这类"简单值",值可以是列表
打印 None get 找不到键返回了空值 不是报错,想给个兜底值就在 get 加第二参数

收个尾

今天的新概念就一个:字典是"键 → 值"的对应关系,靠名字(键)找数据,不靠下标。取值默认 get("键", 默认值);增改是同一个动作 字典["键"] = 值;删用 pop("键");遍历最常用 for key, value in 字典.items():。最后那句选择标准,今天先背下来:存"一个东西的多个属性"用字典。

明天学集合和组合结构。先学集合——自动去重的"麻袋",给列表洗一遍就剩不重复的;再学本周最重要的组合——列表套字典,一个字典列表就是一张数据表格,以后所有项目的数据都长这样。