字典常用方法

和列表、字符串一样,字典也内置了很多方法供我们使用,调用方法时,写成 字典名.方法名() 的形式,字典名和方法名之间要用 . 连接。

下面的字典 students 以 人名-成绩 键值对的形式,保存了这次编程考试中所有同学的成绩。接下来我们以它为例,了解一下字典都有哪些常用方法吧~

# 编程考试成绩
students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}

get()

在上一关中我们学到,可以通过 字典[键] 的方式获取字典中  对应的 。而当  不存在,程序会报错,这影响到我们后续代码的执行。

事实上,Python 为字典提供了一种更安全的查询方法,get() 方法。

将字典的键作为参数传入 get() 方法中,它就会帮我们查询字典中有没有这个键。如果存在的话,返回键对应的值;不存在的话,默认返回 None

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
print(students.get('林黛玉'))
# 输出:95
print(students.get('小小'))
# 输出:None

keys()

keys() 方法则可以获取字典中所有的 

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
names = students.keys()
print(names)
# 输出:dict_keys(['林黛玉', '薛宝钗', '贾宝玉', '袭人'])
print(type(names))
# 输出:<class 'dict_keys'>

可以看到,在 Python3 中 keys() 方法返回的是一个特殊类型 dict_keys

当我们将 students.keys() 的结果保存到变量 names 后,可以通过成员运算符 in 判断某个元素在不在里面,也可以使用 for 循环遍历它。

if '林黛玉' in names:
  print('林黛玉的成绩已录入')
# 输出:林黛玉的成绩已录入
for name in names:
  print(name)
# 输出:林黛玉 薛宝钗 贾宝玉 袭人

但它是不能被索引的。如果想通过索引访问元素,需要先用 list() 函数将它转换成列表类型,再进行访问。

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
names = list(students.keys())
print(names[0])
# 输出:林黛玉

values()

与 keys() 方法相对应,我们可以通过 values() 方法获取字典中所有的 

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
scores = students.values()
print(scores)
# 输出:dict_values([95, 93, 78, 85])
print(type(scores))
# 输出:<class 'dict_values'>

values() 方法返回的也是一个特殊类型,不可索引,可遍历,所以一般搭配 for 循环使用。

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
scores = students.values()
for score in scores:
  print(score)
# 输出:95 93 78 85

items()

除了获取所有的键、值,我们也可以通过 items() 方法,一次性获取字典中所有的 键值对 ,其中每个键值对都是一个形如 (键, 值) 的元组。items() 方法返回的也是一个特殊类型,不可索引,可遍历。

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
student = students.items()
print(student)
# 输出:dict_items([('林黛玉', 95), ('薛宝钗', 93), ('贾宝玉', 78), ('袭人', 85)])
print(type(student))
# 输出:<class 'dict_items'>

(键, 值) 元组构成的序列应该怎么遍历呢?老师给你准备了一个例子。在下面的 for 循环中,name 和 score 分别取到了每个键值对中的键、值。

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
# 以 name, score 遍历 students 中键值对
for name, score in students.items():
  print('{}的分数是:{}'.format(name, score))

# 输出:
# 林黛玉的分数是:95
# 薛宝钗的分数是:93
# 贾宝玉的分数是:78
# 袭人的分数是:85

上面是在使用字典时最常用的几个方法,我们再来归纳总结一下:

编程练习

字典 students 保存了本次编程考试成绩。我们需要根据每个人的成绩,写下不同的评语。

  1. 分数大于 90:打印 xxx 考得很好继续保持
  2. 分数在 80 和 90 之间:打印 xxx 考得不错继续加油
  3. 分数小于等于 80:打印 xxx 考得不行下次努力

其中 xxx 表示人名。

趁热打铁,下面请你用 for 循环和 items() 方法遍历字典完成任务吧~

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
# 以 name, score 遍历 students 中键值对

优化后代码:

students = {
  '林黛玉': 95,
  '薛宝钗': 93,
  '贾宝玉': 78,
  '袭人': 85
}
# 以 name, score 遍历 students 中键值对
for name,score in students.items():
  if score > 90:
    print('{}考得很好继续保持'.format(name))
  elif score > 80:
    print('{}考得不错继续加油'.format(name))
  else:
    print('{}考得不行下次努力'.format(name))

# 代码输出结果为:
# 林黛玉考得很好继续保持
# 薛宝钗考得很好继续保持
# 贾宝玉考得不行下次努力
# 袭人考得不错继续加油

字典的嵌套

学了那么多字典的常用方法,还是要落回到实际问题和应用上来。

在编程世界里,每一种数据类型,都是为了更好地表示现实世界中的信息:我们用 整数浮点数 类型描述数字,用 字符串 类型保存文本,用 布尔 类型表示真假的概念,用 列表 类型存放大量有顺序的数据……这些类型也可以结合起来使用,表示复杂的信息。

🤔️ 那 字典 类型适合表示现实中的哪些信息呢?它和其它数据类型可以结合起来使用吗?下面让老师带你走进几个生活中的场景,看看字典与列表间是怎样相互配合表示数据的。

列表中嵌套字典

我们来看看第一个场景:保存编程考试的成绩。

  • 编程一班:林黛玉 95 分;贾宝玉 78 分;
  • 编程二班:薛宝钗 93 分;袭人 85 分。

把同学的成绩看成 姓名-成绩 键值对的话,相信你很快就能写出这样的代码:

# 编程一班
class_1_score = {'林黛玉': 95, '贾宝玉': 78}
# 编程二班
class_2_score = {'薛宝钗': 93, '袭人': 85}

但这么做有个很麻烦的地方:班级数量一多,变量也会增多,很不方便管理。为了统一管理多个字典变量,我们可以把它们放进一个列表 scores 中,形成 列表中嵌套字典 的结构。这样我们就不用为每个班级单独定义字典变量了~

scores = [
  # 编程一班
  {'林黛玉': 95, '贾宝玉': 78},
  # 编程二班
  {'薛宝钗': 93, '袭人': 85}
]

在访问这种嵌套结构的内层数据时,需要按照它的 层级 一步一步访问。比如我们要获取林黛玉的成绩,要先通过索引,访问到列表 scores 中保存的第一个字典。

print(scores[0])

# 输出:{'林黛玉': 95, '贾宝玉': 78}

再通过 '林黛玉' 键,就能获取到她的成绩啦。

print(scores[0]['林黛玉'])
# 输出:95

我们来用一张图看一下列表 scores 的结构:

遍历时也是一样的道理,需要按照 层级关系,编写一个双重循环,在外层循环里遍历外层结构,内层循环里遍历内层结构。比如我们可以这样打印出每位同学的成绩:

scores = [
  # 编程一班
  {'林黛玉': 95, '贾宝玉': 78},
  # 编程二班
  {'薛宝钗': 93, '袭人': 85}
]
# 遍历列表 scores 中每个班级成绩册
for class_score in scores:
  # 遍历成绩册中每位同学对应分数
  for score in class_score.values():
    print(score)
    
# 输出:95 78 93 85

列表中能嵌套字典,那字典中可以嵌套列表吗?

答案是可以的~字典类型中,每个  对应的  可以是各种各样的数据类型。之前我们已经学习过了值是 整数浮点数字符串 类型的例子,其实,字典的值也可以是 列表元组,甚至是 字典 这样复杂结构的类型。

字典中嵌套列表

我们来看看第二个场景:保存班级名册。

  • 编程一班:林黛玉、贾宝玉;
  • 编程二班:薛宝钗、袭人。

列表类型很适合保存大量、有序的数据,所以我们可以为每个班级创建一个列表变量,保存班里同学的名字。

# 编程一班
class_1 = ['林黛玉', '贾宝玉']
# 编程二班
class_2 = ['薛宝钗', '袭人']

😵 但这样会遇到同样的问题:班级数量增多时,很难统一管理这些变量。那该如何把这些数据整合起来呢?

由于班级和它的名册之间,可以看成 班级-名册 的键值对,所以我们可以创建一个字典 students,把它们整合起来,形成 字典中嵌套列表 的结构。这样一来不用专门为每个列表取变量名,二来可以通过易读易懂的键,方便地访问某个班级对应的名册。

students = {
  '编程一班': ['林黛玉', '贾宝玉'],
  '编程二班': ['薛宝钗', '袭人']
}

和刚刚学习的 列表中嵌套字典 结构一样,对于在 字典中嵌套列表 的 students,也需要一层一层地去访问内部数据。比如我们可以通过 '编程一班' 键访问到它的名册。进一步地,通过索引 0 获取第一位同学的名字。

print(students['编程一班'])
# 输出:['林黛玉', '贾宝玉']
print(students['编程一班'][0])
# 输出:林黛玉

如果想打印出每位同学的名字,则可以写成这样的双重循环,在外层遍历每个班级的名册,在内层遍历名册中每个名字。

students = {
  '编程一班': ['林黛玉', '贾宝玉'],
  '编程二班': ['薛宝钗', '袭人']
}

# 遍历每个班级对应的名册
for names in students.values():
  # 遍历名册中每个名字
  for name in names:
    print(name)

# 输出:林黛玉 贾宝玉 薛宝钗 袭人

编程练习

黄帮主临时来编程一班代课,想知道 袭人 在不在一班。你能编程帮他完成判断吗?

要求:若袭人在一班,打印 在编程一班,否则打印 不在编程一班

提示:可以使用成员运算符 in 判断某个元素在不在列表里。

students = {
  '编程一班': ['林黛玉', '贾宝玉'],
  '编程二班': ['薛宝钗', '袭人']
}

编程后:

students = {
  '编程一班': ['林黛玉', '贾宝玉'],
  '编程二班': ['薛宝钗', '袭人']
}
if '袭人' in students.get('编程一班'):
# if '袭人' in students.get['编程一班']:
  print('在编程一班')
else:
  print('不在编程一班')

刚刚我们说了,字典的值可以是字典类型。那这样的结构有什么例子吗?下面我们来看看第三个场景。

字典中嵌套字典

假设我们需要保存这样一本单词书:

  • apple - 释义:苹果;词性:名词;
  • grape - 释义:葡萄;词性:名词。

可以看到单词书里每个单词都有两个类别的信息,这些信息可以看成 类别-内容 的键值对。所以我们可以为每个单词创建一个字典类型变量,保存单词信息。

apple = {'释义': '苹果', '词性': '名词'}
grape = {'释义': '葡萄', '词性': '名词'}

一本单词书里有很多单词,该如何表示这些单词都属于同一本单词书呢?很简单,由于每个单词和它的信息又可以看成 单词-信息 的键值对,所以我们可以用 字典 把这些单词整合起来,形成 字典中嵌套字典 的结构。

dictionary = {
  'apple': {'释义': '苹果', '词性': '名词'},
  'grape': {'释义': '葡萄', '词性': '名词'}
}

获取单词 apple 的释义时,也是一样的道理,需要按照 层级 一层一层地访问,先通过 'apple' 键访问到它的信息,再通过 '释义' 键获取到它的释义。

print(dictionary['apple'])
# 输出:{'释义': '苹果', '词性': '名词'}
print(dictionary['apple']['释义'])
# 输出:苹果

如果想按照 释义-词性 的格式,按行打印出每个单词对应的信息,我们可以写一个 for 循环,遍历 dictionary 字典中每个单词。

# 遍历每个单词对应的信息
for infos in dictionary.values():
  # 获取释义
  meaning = infos['释义']
  # 获取词性
  pos = infos['词性']
  print('{}-{}'.format(meaning, pos))

# 输出:
# 苹果-名词
# 葡萄-名词

编程练习

字典 dictionary 以 单词-信息 键值对的形式表示一本单词书。

请你使用字典的 keys() 方法,统计这本书有多少个单词,并按照 单词书里有 xxx 个单词 的格式打印出来吧。

提示:可以使用 list() 函数将 keys() 方法获取到的结果转换成列表类型,再统计单词个数。

dictionary = {
  'apple': {'释义': '苹果', '词性': '名词'},
  'grape': {'释义': '葡萄', '词性': '名词'},
  'eat': {'释义': '吃', '词性': '动词'},
  'big': {'释义': '大的', '词性': '形容词'}
}

# 获取单词书 dictionary 中的单词列表
# 单词列表为:['apple', 'grape', 'eat', 'big']

# 获取 word_list 的长度,即为单词个数

优化后的代码为:

dictionary = {
  'apple': {'释义': '苹果', '词性': '名词'},
  'grape': {'释义': '葡萄', '词性': '名词'},
  'eat': {'释义': '吃', '词性': '动词'},
  'big': {'释义': '大的', '词性': '形容词'}
}

# 获取单词书 dictionary 中的单词列表
# 单词列表为:['apple', 'grape', 'eat', 'big']
word_list = len(dictionary.keys())
# 获取 word_list 的长度,即为单词个数
print('单词书里有{}个单词'.format(word_list))

# 输出为:
# 单词书里有4个单词

编程练习二_摇骰子

大家肯定都摇过 🎲,我们知道一个骰子有六个面,分别对应 1-6 六个数字,这六个数字出现的概率是一样的,都是六分之一(0.166666...)。

还记得我们学过的 random 模块吗?为 random.choice() 函数传入一个非空序列,它会从中随机选取一个元素并返回。

接下来请你使用 random.choice() 函数模拟摇骰子的过程,统计每个数字出现的次数,并按照 数字 x 出现的频率为 xxx 的格式,打印出每个数字出现的频率吧。

提示:可以通过 数字出现次数 / 摇骰子总次数 计算出每个数字出现的频率。

# 导入 random 模块
import random

counts = {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0}
# 取出 counts 中所有键,并转换成列表类型
nums = ???
# 摇骰子次数
times = 5000
# 次数越多,每个数字出现的频率越接近于它的概率
# 你可以改成更大的数字试试,但耗时会更长

# 模拟摇骰子的过程并计数

# 打印每个数字出现的频率

代码优化完后:

# 导入 random 模块
import random

counts = {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0}
# 取出 counts 中所有键,并转换成列表类型

# 摇骰子次数
times = 5000
# 次数越多,每个数字出现的频率越接近于它的概率
# 你可以改成更大的数字试试,但耗时会更长
# 模拟摇骰子的过程并计数
for i in range(times):
  nums = random.choice(list(counts))
  # 灵活的方法
  counts[nums] = counts[nums] + 1
  # 笨方法
  # if nums == '1':
  #   counts['1'] = counts['1'] + 1
  # elif nums == '2':
  #   counts['2'] = counts['2'] + 1
  # elif nums == '3':
  #   counts['3'] = counts['3'] + 1
  # elif nums == '4':
  #   counts['4'] = counts['4'] + 1
  # elif nums == '5':
  #   counts['5'] = counts['5'] + 1
  # else:
  #   counts['6'] = counts['6'] + 1
# print(counts)
# 打印每个数字出现的频率
for i in counts:
  chance = counts[i] / times
  print('数字{}出现的概率为{}'.format(i, chance))

编程练习三:社团迎新会

又到了一年一度的社团招新季,编程社、话剧团和读书会这三个社团,计划一同举办社团迎新活动,欢迎新加入的小伙伴。

迎新会海报上,需要打印出各个社团的基本信息。字典 associations_dict 中,以 字典嵌套字典 的结构储存了各个社团的基本信息。下面请你按如下格式将社团信息打印出来:

参会社团信息:

------------

名称:编程社

社长:黄帮主

副社长:小贝

------------

名称:话剧团

社长:柚子

副社长:柠檬

------------

名称:读书会

社长:小林

副社长:多多

注意:由于 Python 中字典元素具有无序性,你的输出的顺序可能跟老师给出的范例不一样,因此本道题仅检查输出格式,不要求顺序完全相同。

associations = {
  '编程社': {'社长': '黄帮主', '副社长': '小贝'},
  '话剧团': {'社长': '柚子', '副社长': '柠檬'},
  '读书会': {'社长': '小林', '副社长': '多多'}
}

print('参会社团信息:')
# 以 association, infos 遍历 associations
for ???, ??? in ???:
  # 打印分割线
  print('------------')
  # 打印社团名称
  
  # 以 title, name 遍历 infos

优化后的代码与输出为:

associations = {
  '编程社': {'社长': '黄帮主', '副社长': '小贝'},
  '话剧团': {'社长': '柚子', '副社长': '柠檬'},
  '读书会': {'社长': '小林', '副社长': '多多'}
}

print('参会社团信息:')
# 以 association, infos 遍历 associations
for association,infos in associations.items():
  # 打印分割线
  print('------------')
  # 打印社团名称
  print('名称:{}'.format(association))
  # 以 title, name 遍历 infos
  for title,name in infos.items():
    print('{}:{}'.format(title,name))

# 输出效果为:
# 参会社团信息:
# ------------
# 名称:编程社
# 社长:黄帮主
# 副社长:小贝
# ------------
# 名称:话剧团
# 社长:柚子
# 副社长:柠檬
# ------------
# 名称:读书会
# 社长:小林
# 副社长:多多

看完了上面几个例子,我们可以总结一下什么时候适合使用列表类型存储数据,什么时候又适合用字典存储:

如果每个元素间是并列关系,或者有先后顺序,适合用 列表 存储。

# 自然数
list1 = [0, 1, 2, 3]
# 学生名字
list2 = ['林黛玉', '贾宝玉', '薛宝钗', '袭人']
# 考试成绩
list3 = [95, 78, 93, 85]

如果数据与数据之间存在对应关系,则适合用 字典 存储。

# 单词-信息
apple = {'释义': '苹果', '词性': '名词'}
# 学生-成绩
class_1_score = {'林黛玉': 95, '贾宝玉': 78}
# 人-年龄
age = {'闻闻': 23, '小贝': 22}

当然,现实生活中的数据往往是复杂的,这时我们可以根据数据的特点,将两个类型结合起来。不同的数据类型,就像是“武器库”中各式各样的“武器”。每种“武器”都有自己的效果,我们要合理利用,才能充分发挥它们的优势!

如果你想知道如何在现实生活中运用各类“武器”,那一定不要错过选修内容中的进阶实操项目 游戏抽卡模拟器。在那里,我会带你运用模块、字典知识,从零开始编写一款抽卡模拟器,并为你拓展游戏“保底机制”实现原理。٩(˃̶͈̀௰˂̶͈́)و 相信完成项目后,你会对自己平时玩的游戏有更深刻的理解。

下章预告:

从下章开始,我们将走进 面向对象编程,了解什么是 对象,学会分析 对象 的共性,把这些共性抽象出来。在此基础上,我们还将学习如何为每一个不同的个体赋予它独有的个性。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐