多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[Toc] # 第7章 dict的使用 又是一个周末!最近墨博士一直出差,小墨也忙于做暑假作业,就没能继续学Python,这天,墨哥哥看小墨作业已经差不多做完了,就想着再给小墨讲讲Python的内容。墨大侠都讲了哪些东西呢?一起来看看吧。 时间:周日上午 地点:小墨家 墨哥哥:小墨,今天呢我再给你补充一些Python的内容怎么样? 小墨:太好啦!我几天没看Python,都有点想它了。 墨哥哥:好,那我以咱们家的一些书为例,来给你说说Python中dict数据类型的用法。 # [插画:书架,类似下图] ![](https://img.kancloud.cn/7e/0c/7e0cb84d5a3270faa877613c2054277b_1200x808.png) ## 7.1 为什么要使用dict 墨哥哥:比如现在有两个list: ``` list1 = ['朝花夕拾', '繁星春水', '骆驼祥子', '西游记', '水浒传', '三国演义'] list2 = ['鲁迅', '冰心', '老舍', '吴承恩', '施耐庵', '罗贯中'] ``` 两个list分别存储名著和名著的作者,并且名著和其对应作者在list中的顺序一致。 此时想查找“骆驼祥子”的作者是谁,应该如何做? 小墨:嗯……,因为名著和其作者在两个list中的顺序一致,可以先找到“骆驼祥子”在“list1”中的顺序,也就是下标,然后根据此下标从“list2”中取作者,就是“骆驼祥子”的作者啦。 墨哥哥:思路对了。参考代码如下: ``` for i in range(len(list1)): if list1[i] == '骆驼祥子': print(list2[i]) ``` 注意这里的字符串是否相等的比较,也使用==就行。 接下来,考你几个问题,听好了小墨。 如果要添加新的名著和作者进来,比如添加“红楼梦”和“曹雪芹”进来,如何做? 小墨:list1中添加名著,list2中对应添加作者。 墨哥哥:那如果想删除某个名著呢? 小墨:list1中删除名著,list2中删除作者。 墨哥哥:添加时,如果添加名著成功了,但是添加作者失败了,或者删除时,删除名著成功了,但是删除作者失败了,怎么办? 小墨:…… 墨哥哥:这样的话,最大的问题就是原本顺序一一对应的两个列表,一下子全乱掉了。比如原本两个列表中都有1000个值,现在要删除第8个,名著删除成功,list1剩999个数据,作者删除失败list2中还是1000个数据,此时从第8个开始往后的每一个数据都没法再一一对应上了,如果类似这样的问题出现了几次,是不是就全乱套了? 小墨:确实,下标乱了就没法对应上了。这个问题如何解决呢? 墨哥哥:如果两个列表中的数据能统一操作,比如删除成功就都成功,失败就都失败,就好了。 小墨:好了墨哥哥,别卖关子了,赶紧说说怎么解决吧。 墨哥哥:这个可以使用Python中的dict类型来解决。 ## 7.2 dict的写法 墨哥哥:dict,全称是dictionary,字典的意思。这是Python内置的一种数据类型,它不同于list的存储一列数据,它存储两列数据,这也是它能够使数据要么全成功要么全失败的秘诀。 下面我们定义一个dict: ``` book_author_dict = {} ``` 这样就定义好了。“book_author_dict”是我们定义的dict类型的变量的名称,后面跟一对大括号。大括号中可以放入要存储的数据,现在什么都没有,称为空dict。 放入数据时的定义如下: ``` book_author_dict = {'朝花夕拾': '鲁迅'} ``` 此时“{'朝花夕拾': '鲁迅'}”就是字典的内容,大括号中的数据组成“key:value”的形式,key和value之间用英文冒号“:”分割,这里的key称为dict的“键”,value被称为dict的“值”,两个放在一块称为“键值对”。一个dict中可以放多个键值对,键值对和键值对之间用逗号“,”分隔,和list中类似,最后一个键值对后面的逗号可以有也可以没有。比如: ``` book_author_dict = { '朝花夕拾': '鲁迅', '繁星春水': '冰心', '骆驼祥子': '老舍', '西游记': '吴承恩', '水浒传': '施耐庵', '三国演义': '罗贯中' } ``` 这就定义好了一个dict,它由6对键值对组成。 小墨:哦,那这个dict如何添加或删除新数据呢? 墨哥哥:别着急。还记得昨天我跟你说过的list中的数据管理方法有哪些吗? 小墨:增加、插入、删除、修改和查询。 墨哥哥:嗯增加和插入都是增加,所以对数据的管理无外乎就是增删改查了。今天我也从这几个方面来介绍一下dict的用法。 ## 7.3 dict的用法 ### 7.3.1 单个查找 墨哥哥:dict中的查找,是根据key查找value,可以通过dict[key]的格式取得value,例如: ``` # 查找朝花夕拾的作者 print(book_author_dict['朝花夕拾']) ``` 输出: ``` 鲁迅 ``` 也可以通过dict.get(key)的方式取得value: ``` print(book_author_dict.get('朝花夕拾')) ``` 当然,如果key不存在,则查找会报错。 ### 7.3.2 添加 墨哥哥:dict的添加格式是:dict[key] = value,直接将value赋值一个新的key就可以了,如: ``` # 添加新的键值对 book_author_dict['红楼梦'] = '曹雪芹' print(book_author_dict) ``` 直接输出dict本身,输出结果为: ``` {'朝花夕拾': '鲁迅', '繁星春水': '冰心', '骆驼祥子': '老舍', '西游记': '吴承恩', '水浒传': '施耐庵', '三国演义': '罗贯中', '红楼梦': '曹雪芹'} ``` ### 7.3.3 修改 墨哥哥:如果一个key已经存在,这时又给这个key匹配了一个新的value,那么原键值对会被覆盖,这个可以看做是修改功能,比如将朝花夕拾的作者由鲁迅改为周树人: ``` book_author_dict['朝花夕拾'] = '周树人' print(book_author_dict) ``` 输出: ``` {'朝花夕拾': '周树人', '繁星春水': '冰心', '骆驼祥子': '老舍', '西游记': '吴承恩', '水浒传': '施耐庵', '三国演义': '罗贯中', '红楼梦': '曹雪芹'} ``` 从这里我们还可以得出一个结论:**dict中的key不可能重复(重复的被覆盖掉了)** ### 7.3.4 删除 墨哥哥:dict中删除也使用pop()函数,括号中传入key,也即根据key删除整个键值对,如: ``` # 删除红楼梦这个key和曹雪芹这个value book_author_dict.pop('红楼梦') print(book_author_dict) ``` 输出: ``` {'朝花夕拾': '周树人', '繁星春水': '冰心', '骆驼祥子': '老舍', '西游记': '吴承恩', '水浒传': '施耐庵', '三国演义': '罗贯中'} ``` 这样就达到了key和value一对数据一删都删的目的。 ### 7.3.5 查找全部的key 墨哥哥:除了上面的增删改查,dict经常会需要查询全部的key。配合for循环,查找全部的名著名称的方法为: ``` for book in book_author_dict: print(book) ``` ### 7.3.6 查找所有key和value 墨哥哥:如果想查找dict全部的key和value,比如这里的查询全部的名著和作者,则需要使用dict的items()方法,items()方法会得到所有的键值对,我们使用for循环把这些键值对一个一个的取出就可以了。 ``` for book, author in book_author_dict.items(): print('著作:%s,作者:%s' % (book, author)) ``` 好,以上呢就是dict的常见基础用法,怎么样小墨? 小墨:内容都不难,但是我得自己做一遍,加深印象。 墨哥哥:编程就是多多练习。整好我准备了一道题,你来试试吧,做完之后我再带你做个有意思的程序怎么样? 小墨:好啊好啊,我喜欢墨哥哥的游戏。 墨哥哥:好,那先来完成下面的练习吧。 > 练习:查找冰心的著作是什么 小墨:dict中并没有根据value查找key的方法,而是只能根据key查找value,现在要想根据作者查找著作,可以先把原dict中的key和value全部取出来,然后value做key,key做value构建一个新的dict,在新的dict中就可以根据作者查找其著作了。 代码如下: ``` # 图书和作者的字典 book_author_dict = { '朝花夕拾': '鲁迅', '繁星春水': '冰心', '骆驼祥子': '老舍', '西游记': '吴承恩', '水浒传': '施耐庵', '三国演义': '罗贯中' } # 定义一个空的字典, author_book_dict = {} # 交换key和value组成新的字典 for book, author in book_author_dict.items(): author_book_dict[author] = book # 查找冰心的著作名称 print(author_book_dict['冰心']) ``` ## 7.4 list和dict的综合运用 墨哥哥:小墨,前面你已经学习了dict的基础用法,学的不错。现在呢再来看一个复杂的情况: 以 ``` book_author_dict = {'朝花夕拾': '鲁迅'} ``` 为例,dict由key和value构成,也即dict只能存储两列数据,那如果我想存储多列数据,比如书的价格,书的出版社等也想存起来,该怎么办呢? 可以调整一下思路,不再是key存书名,value存作者名,而是将书所有的信息全部作为value,然后给这些信息取个key对应,就像这样: ``` book_dict = {'name': '朝花夕拾', 'author': '鲁迅', 'price': '39.9¥', 'publishing': '北京未名出版社'} ``` 这样的话,一个dict就表示一本书的相关信息。 此时问题就来了:一个dict表示一本书,那如何表示多本书呢? ### 7.4.1 list和dict的嵌套表示 墨哥哥:还记得前面说的list吗?list用于存储一系列的数据,可以把每本书的信息看做是一个整体,然后存到list中来,其大致结构如下: ``` # 第一本书的信息 book_dict_1 = {} # 第二本书的信息 book_dict_2 = {} book_dict_3 = {} ... # 很多本书的信息 # 每本书的信息看做一个整体存在list中。 list = [book_dict_1, book_dict_2, book_dict_3...] ``` 这就是dict作为了list的元素,当然,还能再复杂一点,我们结合具体案例来说。 ### 7.4.2 角色选择功能数据准备 墨哥哥:现在我们来编写一个程序,综合练习下这两者相结合的用法。就来做个墨家村英雄榜吧。 # [插画:墨家村英雄榜,类似下图:] ![](http://7xtc8l.com1.z0.glb.clouddn.com/20180110151738.png) 小墨:墨家村英雄榜是什么,一个新游戏吗? 墨哥哥:很多角色扮演游戏中,用户每次可以选择不同角色,比如三国类的游戏中这一次你可以扮演诸葛亮,下一次你就可以扮演曹操了。我们要做的这个墨家村英雄榜就是模拟游戏中玩家挑选角色的功能。 小墨:哦,我懂了。用字典存储角色的信息然后供玩家去挑选。 墨哥哥:真是聪明的小墨!下面我们开始。先来将1个角色的数据表示出来,我们以墨小小这个角色为例。 小墨:我又成游戏的主角了。 墨哥哥:还有你的小伙伴呢。 以墨小小为例,他有4个技能,这4个技能可以存成list也可以存成dict,这里我们存成list吧: ``` skills = ['一墨横空', '墨渡迷津', '墨之纵横', '墨下乾坤'] ``` 然后是墨小小的基本信息,如姓名、生命值、攻击力防御力等,可以定义普通变量表示,我们随便给个数值,如下: ``` name = '墨小小' # 姓名 hp = 1000 # 血量 mp = 800 # 魔法量 ap = 45 # 攻击力 dp = 20 # 防御力 ``` 如果用一个dict表示墨小小,也即既有墨小小的基本信息,又有墨小小的技能列表,可以这样做: ``` hero = { 'name': '墨小小', 'hp': 1000, 'mp': 800, 'ap': 45, 'dp': 20, 'skills': ['一墨横空', '墨渡迷津', '墨之纵横', '墨下乾坤'], } ``` 这就是将list作为dict的元素了。 小墨:哦,原来dict中的value可以是不同的类型,并且还可以是list类型呢。 墨哥哥:是的。好了,上面是墨小小一个人的信息,如果想表示多个人呢?就是将上述dict作为一个整体看,放入list中,如下: ``` hero1 = { 'name': '墨小小', 'hp': 1000, 'mp': 800, 'ap': 45, 'dp': 20, 'skills': ['一墨横空', '墨渡迷津', '墨之纵横', '墨下乾坤'], } hero2 = { 'name': '墨小妹', 'hp': 800, 'mp': 1000, 'ap': 50, 'dp': 18, 'skills': ['貂蝉拜月', '西施捧心', '昭君出塞', '贵妃醉酒'], } hero_list = [hero1, hero2] ``` 或者直接一步到位,省去定义变量的麻烦: ``` hero_list = [{ 'name': '墨小小', 'hp': 1000, 'mp': 800, 'ap': 45, 'dp': 20, 'skills': ['一墨横空', '墨渡迷津', '墨之纵横', '墨下乾坤'], }, { 'name': '墨小妹', 'hp': 800, 'mp': 1000, 'ap': 50, 'dp': 18, 'skills': ['貂蝉拜月', '西施捧心', '昭君出塞', '贵妃醉酒'], }] ``` 这时就是list中包含dict,dict中还包含有list的结构了。 小墨:这样来看结构还挺复杂的。 墨哥哥:嗯。游戏中的角色一般都会按照职业分类,比如战士、法师等。接下来我们先来开发分类查找的功能,也即能在所有角色中把全部战士找出来,或把全部法师找出来等。 小墨:那要如何才能知道哪些角色是同一类的呢? 'is_warrior': False, 'is_mage': False, 'is_hunter' 墨哥哥:问的好!为了能够分类,我们需要将每个角色所属的职业标示出来,可以用is_warrior表示是否是战士,is_mage表示是否是法师,is_hunter表示是否是猎人,战士、法师和猎人都是角色的种类。单个角色的数据如下: ``` { 'name': '墨小小', 'hp': 1000, 'mp': 800, 'ap': 45, 'dp': 20, 'skills': ['一墨横空', '墨渡迷津', '墨之纵横', '墨下乾坤'], 'is_warrior': True, 'is_mage': False, 'is_hunter': False } ``` 小墨:哦,这样一个角色就能属于多个分类了,如即使坦克又是战士。 墨哥哥:是的。接着我们定义5个角色出来: ``` hero_list = [ {'name': '墨小小', 'hp': 1000, 'mp': 800, 'ap': 45, 'dp': 20, 'skills': ['一墨横空', '墨渡迷津', '墨之纵横', '墨下乾坤'], 'is_warrior': True, 'is_mage': False, 'is_hunter': False, }, {'name': '墨小妹', 'hp': 1200, 'mp': 700, 'ap': 35, 'dp': 21, 'skills': ['貂蝉拜月', '西施捧心', '昭君出塞', '贵妃醉酒'], 'is_warrior': True, 'is_mage': True, 'is_hunter': False, }, {'name': '墨大元', 'hp': 1100, 'mp': 600, 'ap': 38, 'dp': 17, 'skills': ['千里横行', '寒刀断水', '狂龙破日', '天地无情'], 'is_warrior': True, 'is_mage': False, 'is_hunter': True, }, {'name': '墨当归', 'hp': 900, 'mp': 1100, 'ap': 44, 'dp': 17, 'skills': ['流水行云', '披云戴月', '翻云覆雨', '排山倒海'], 'is_warrior': False, 'is_mage': True, 'is_hunter': False, }, {'name': '墨鱼儿', 'hp': 1000, 'mp': 1000, 'ap': 42, 'dp': 23, 'skills': ['小楫轻舟', '扁舟一叶', '大江似练', '沧波万顷'], 'is_warrior': False, 'is_mage': False, 'is_hunter': True, } ] ``` 小墨:哇!小妹的技能好厉害! ### 7.4.3 查找所有战士的姓名 墨哥哥:接下来说说功能。我们知道,使用for循环可以把list中的每个元素都拿出来,方法是使用for x in list,这里得到的每个x就是list中的每个元素。 现在你看,hero_list就是一个list,我们当然可以使用for循环拿出它的每一个元素。拿出来的每一个元素是什么呢? 小墨:hero_list中存的是5个角色,拿出的元素就是一个个的角色吧? 墨哥哥:是的,而每一个角色的数据本身又是一个dict,也即拿出来的是5个dict。 针对每一个dict来说,我们可以通过is_warrior这个key取对应的value,如果是True,说明这个角色是战士,此时我们再次通过name这个key取出它的名字就可以了。代码如下: ``` for x in hero_list: if x.get('is_warrior'): print(x.get('name')) ``` 输出结果为: ``` 墨小小 墨小妹 墨大元 ``` 小墨:哈哈墨妹妹也是战士了。 墨哥哥:自己编写的程序自己做主! ### 7.4.4 获取所有战士的技能列表 墨哥哥:现在我们来获取所有战士的技能列表。 上面的代码中x.get('name')就获取了所有战士的名字,那么我们把这里的name改成skills就可以获取所有战士的技能列表了。 ``` for x in hero_list: if x.get('is_warrior'): print(x['skills']) # get或[],都可以通过key取value ``` 但是,此时x['skills']拿到的技能列表是个list类型,也即“['一墨横空', '墨渡迷津', '墨之纵横', '墨下乾坤']”,如果是想拿到具体的一个个技能,还需要对这个list再次循环取出。程序变成了: ``` for x in hero_list: if x.get('is_warrior'): print('*' * 10) print(x.get('name') + '的技能有:') for skill in x['skills']: print(skill) ``` 输出结果为: ``` ********** 墨小小的技能有: 一墨横空 墨渡迷津 墨之纵横 墨下乾坤 ********** 墨小妹的技能有: 貂蝉拜月 西施捧心 昭君出塞 贵妃醉酒 ********** 墨大元的技能有: 千里横行 寒刀断水 狂龙破日 天地无情 ``` 这里的“print('\*' * 10)”作用是输出10个星号,分割每个角色的技能列表,这样方便我们观察输出。你看,Python中的乘法不光是数学上的相乘,还能用于字符串和数字相乘,表示复制出来多少份字符串。 现在的程序就写成了for循环里嵌套了for循环,也称为双重for循环。小墨,你之前见过双重for循环吗? 小墨:没有。 墨哥哥:那看下这个案例,能更好的理解双重for循环的执行过程: ``` for i in ['a', 'b', 'c']: for j in ['1', '2', '3']: print(i + j) ``` 运行输出结果为: ``` a1 a2 a3 b1 b2 b3 c1 c2 c3 ``` 你试着来分析一下吧。 小墨:我看看……,首先,外层的for循环会从['a', 'b', 'c']中取出a赋值给i,然后执行后面跟的代码块,现在整个内部for循环充当了代码块,所以会执行内部for循环。内部for循环是从['1', '2', '3']中依次取出每一个元素,通过+号跟i拼接,就会得到a1、a2和a3,此时内部for循环运行结束,也即外部for循环的第一次循环结束。程序进入下一次的外部循环中,i被赋值为了b,然后再次执行内部循环。依次类推,最终输出就是上面那种了。 墨哥哥:嗯分析清楚这个之后再看上面获取角色列表的程序就会清晰多了。双重for循环的作用就是将所有符合条件的角色的技能列表输出了出来。 ### 7.4.5 根据输入查找某个角色的血量 墨哥哥:再来看最后一个功能:根据输入的姓名查找某个角色的血量,这个怎么做呢? 小墨:可以将用户输入的姓名和所有的角色姓名做比对,如果有名字一样的,就把对应的血量输出出来: ``` name = input('请输入要查找的英雄的姓名:') for x in hero_list: if x.get('name') == name: print(name + '的血量为:%d' % x.get('hp')) ``` 输出: ``` 请输入要查找的英雄的姓名:墨大元 墨大元的血量为:1100 ``` 墨哥哥:问题是,有些搜索,需要能够模糊查询。所谓模糊查询,就比如我输入“大”,应该把“墨大元”给找出来,显然这里的条件判断x.get('name') == name是精确匹配,也即要查找“墨大元”必须输入“墨大元”才查找的出来。 小墨:那如何才能模糊查询呢? 墨哥哥:如果我们能判断一个字符串中是否包含另一个字符串就行了。这在Python中有很多种方法,常用的有: 1、字符串中的“in”操作,如a in b,表示a是否在b中,也即b是否包含a,返回True或False ``` name = '墨大元' if '大' in name: print('匹配') ``` 2、字符串可以看作是字符的列表,也有对应下标的概念。可以使用find()方法,查找字符串中是否有某个子字符串,该方法返回该子字符串在字符串中出现的位置(下标) 如'abc'.find('a')得到0,'abc'.find('b')则得到1,如果找不到,则方法返回-1。使用find()来模糊查询代码如下: ``` name = '墨大元' if name.find('大') != -1: print('匹配') ``` 3、和find()类似,也可以通过index()方法,该方法专门用于查找子字符串的下标,如果找不到则返回-1,index使用如下: ``` name = '墨大元' if name.index('大') != -1: print('匹配') ``` 墨哥哥提醒:find和index都要和-1比较。因为子字符串有可能出现在第一位,此时下标为0,Python中0作为条件时就表示False。 小墨,使用上面三种方法中的任意一种,为上述小助手提供模糊查询功能吧。 > 动动手:请计算上述五种英雄的平均血量,注意避免“hard coding” ## 7.5 本章小结 墨哥哥总结:今天你主要学习了dict的基本用法,和list一样,基本用法也是增删改查。其中查询可能是根据某一key查找对应value;也可能是查询所有的key;也有可能是查询所有的key和value。之后做了一个墨家村英雄榜的程序。这个案例是dict和list的相互嵌套,对你来说可能有点复杂,但同时也说明了你已经成长了很多不是吗。加油!学的越多,能做出的程序也就越好玩,越强大!