字典,这个东西你现在还用吗?随着网络的发展,用的人越来越少了。不少人习惯于在网上搜索,不仅有web版,乃至于已经有手机版的各种字典了。我在上小学的时候曾经用过一本小小的《新华字典》,记得是拾了不少废品,然后换钱,最终花费了1.01元人民币买的。 > 《新华字典》是中国第一部现代汉语字典。最早的名字叫《伍记小字典》,但未能编纂完成。自1953年,开始重编,其凡例完全采用《伍记小字典》。从1953年开始出版,经过反复修订,但是以1957年商务印书馆出版的《新华字典》作为第一版。原由新华辞书社编写,1956年并入中科院语言研究所(现中国社科院语言研究所)词典编辑室。新华字典由商务印书馆出版。历经几代上百名专家学者10余次大规模的修订,重印200多次。成为迄今为止世界出版史上最高发行量的字典。 这里讲到字典,不是为了回忆青葱岁月。而是提醒看官想想我们如何使用字典:先查索引(不管是拼音还是偏旁查字),然后通过索引找到相应内容。不用从头开始一页一页地找。 这种方法能够快捷的找到目标。 正是基于这种需要,python中有了一种叫做dictionary的数据类型,翻译过来就是“字典”,用dict表示。 假设一种需要,要存储城市和电话区号,苏州的区号是0512,唐山的是0315,北京的是011,上海的是012。用前面已经学习过的知识,可以这么来做: ~~~ >>> citys = ["suzhou", "tangshan", "beijing", "shanghai"] >>> city_codes = ["0512", "0315", "011", "012"] ~~~ 用一个列表来存储城市名称,然后用另外一个列表,一一对应地保存区号。假如要输出苏州的区号,可以这么做: ~~~ >>> print "{} : {}".format(citys[0], city_codes[0]) suzhou : 0512 ~~~ > 请特别注意,我在city_codes中,表示区号的元素没有用整数型,而是使用了字符串类型,你知道为什么吗? 如果用整数,就是这样的。 ~~~ >>> suzhou_code = 0512 >>> print suzhou_code 330 ~~~ > 怎么会这样?原来在python中,如果按照上面那样做,0512并没有被认为是一个八进制的数,用print打印的时候,将它转换为了十进制输出。关于进制转换问题,看官可以网上搜索一下有关资料。此处不详述。一般是用几个内建函数实现:`int()`, `bin()`, `oct()`, `hex()` 这样来看,用两个列表分别来存储城市和区号,似乎能够解决问题。但是,这不是最好的选择,至少在python里面。因为python还提供了另外一种方案,那就是字典(dict)。 ## [](https://github.com/qiwsir/StarterLearningPython/blob/master/116.md#创建dict)创建dict **方法1:** 创建一个空的dict,这个空dict,可以在以后向里面加东西用。 ~~~ >>> mydict = {} >>> mydict {} ~~~ 不要小看“空”,“色即是空,空即是色”,在编程中,“空”是很重要。一般带“空”字的人都很有名,比如孙悟空,哦。好像他应该是猴、或者是神。举一个人的名字,带“空”字,你懂得。 创建有内容的dict。 ~~~ >>> person = {"name":"qiwsir","site":"qiwsir.github.io","language":"python"} >>> person {'name': 'qiwsir', 'language': 'python', 'site': 'qiwsir.github.io'} ~~~ `"name":"qiwsir"`,有一个优雅的名字:键值对。前面的name叫做键(key),后面的qiwsir是前面的键所对应的值(value)。在一个dict中,键是唯一的,不能重复。值则是对应于键,值可以重复。键值之间用(:)英文的分号,每一对键值之间用英文的逗号(,)隔开。 ~~~ >>> person['name2']="qiwsir" #这是一种向dict中增加键值对的方法 >>> person {'name2': 'qiwsir', 'name': 'qiwsir', 'language': 'python', 'site': 'qiwsir.github.io'} ~~~ 用这样的方法可以向一个dict类型的数据中增加“键值对”,也可以说是增加数值。那么,增加了值之后,那个字典还是原来的吗?也就是也要同样探讨一下,字典是否能原地修改?(列表可以,所以列表是可变的;字符串和元组都不行,所以它们是不可变的。) ~~~ >>> ad = {} >>> id(ad) 3072770636L >>> ad["name"] = "qiwsir" >>> ad {'name': 'qiwsir'} >>> id(ad) 3072770636L ~~~ 实验表明,字典可以原地修改,即它是可变的。 **方法2:** 利用元组在建构字典,方法如下: ~~~ >>> name = (["first","Google"],["second","Yahoo"]) >>> website = dict(name) >>> website {'second': 'Yahoo', 'first': 'Google'} ~~~ 或者用这样的方法: ~~~ >>> ad = dict(name="qiwsir", age=42) >>> ad {'age': 42, 'name': 'qiwsir'} ~~~ **方法3:** 这个方法,跟上面的不同在于使用fromkeys ~~~ >>> website = {}.fromkeys(("third","forth"),"facebook") >>> website {'forth': 'facebook', 'third': 'facebook'} ~~~ 需要提醒的是,这种方法是重新建立一个dict。 需要提醒注意的是,在字典中的“键”,必须是不可变的数据类型;“值”可以是任意数据类型。 ~~~ >>> dd = {(1,2):1} >>> dd {(1, 2): 1} >>> dd = {[1,2]:1} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list' ~~~ ## [](https://github.com/qiwsir/StarterLearningPython/blob/master/116.md#访问dict的值)访问dict的值 dict数据类型是以键值对的形式存储数据的,所以,只要知道键,就能得到值。这本质上就是一种映射关系。 > 映射,就好比“物体”和“影子”的关系,“形影相吊”,两者之间是映射关系。此外,映射也是一个严格数学概念:A是非空集合,A到B的映射是指:A中每个元素都对应到B中的某个元素。 既然是映射,就可以通过字典的“键”找到相应的“值”。 ~~~ >>> person {'name2': 'qiwsir', 'name': 'qiwsir', 'language': 'python', 'site': 'qiwsir.github.io'} >>> person['name'] 'qiwsir' >>> person['language'] 'python' ~~~ 如同前面所讲,通过“键”能够增加dict中的“值”,通过“键”能够改变dict中的“值”,通过“键”也能够访问dict中的“值”。 本节开头那个城市和区号的关系,也可以用字典来存储和读取。 ~~~ >>> city_code = {"suzhou":"0512", "tangshan":"0315", "beijing":"011", "shanghai":"012"} >>> print city_code["suzhou"] 0512 ~~~ 既然dict是键值对的映射,就不用考虑所谓“排序”问题了,只要通过键就能找到值,至于这个键值对位置在哪里就不用考虑了。比如,刚才建立的city_code ~~~ >>> city_code {'suzhou': '0512', 'beijing': '011', 'shanghai': '012', 'tangshan': '0315'} ~~~ 虽然这里显示的和刚刚赋值的时候顺序有别,但是不影响读取其中的值。 在list中,得到值是用索引的方法。那么在字典中有索引吗?当然没有,因为它没有顺序,哪里来的索引呢?所以,在字典中就不要什么索引和切片了。 > dict中的这类以键值对的映射方式存储数据,是一种非常高效的方法,比如要读取值得时候,如果用列表,python需要从头开始读,直到找到指定的那个索引值。但是,在dict中是通过“键”来得到值。要高效得多。 正是这个特点,键值对这样的形式可以用来存储大规模的数据,因为检索快捷。规模越大越明显。所以,mongdb这种非关系型数据库在大数据方面比较流行了。 ## [](https://github.com/qiwsir/StarterLearningPython/blob/master/116.md#基本操作)基本操作 字典虽然跟列表有很大的区别,但是依然有不少类似的地方。它的基本操作: * len(d),返回字典(d)中的键值对的数量 * d[key],返回字典(d)中的键(key)的值 * d[key]=value,将值(value)赋给字典(d)中的键(key) * del d[key],删除字典(d)的键(key)项(将该键值对删除) * key in d,检查字典(d)中是否含有键为key的项 下面依次进行演示。 ~~~ >>> city_code {'suzhou': '0512', 'beijing': '011', 'shanghai': '012', 'tangshan': '0315'} >>> len(city_code) 4 ~~~ 以city_code为操作对象,len(city_code)的值是4,表明有四组键值对,也可以说是四项。 ~~~ >>> city_code["nanjing"] = "025" >>> city_code {'suzhou': '0512', 'beijing': '011', 'shanghai': '012', 'tangshan': '0315', 'nanjing': '025'} ~~~ 向其中增加一项 ~~~ >>> city_code["beijing"] = "010" >>> city_code {'suzhou': '0512', 'beijing': '010', 'shanghai': '012', 'tangshan': '0315', 'nanjing': '025'} ~~~ 突然发现北京的区号写错了。可以这样修改。这进一步说明字典是可变的。 ~~~ >>> city_code["shanghai"] '012' >>> del city_code["shanghai"] ~~~ 通过`city_code["shanghai"]`能够查看到该键(key)所对应的值(value),结果发现也错了。干脆删除,用del,将那一项都删掉。 ~~~ >>> city_code["shanghai"] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'shanghai' >>> "shanghai" in city_code False ~~~ 因为键是"shanghai"的那个键值对项已经删除了,随意不能找到,用`in`来看看,返回的是`False`。 ~~~ >>> city_code {'suzhou': '0512', 'beijing': '010', 'tangshan': '0315', 'nanjing': '025'} ~~~ 真的删除了哦。没有了。 ## [](https://github.com/qiwsir/StarterLearningPython/blob/master/116.md#字符串格式化输出)字符串格式化输出 这是一个前面已经探讨过的话题,请参看[《字符串(4)》](https://github.com/qiwsir/StarterLearningPython/blob/master/109),这里再次提到,就是因为用字典也可以实现格式化字符串的目的。虽然在《字符串(4)》那节中已经有了简单演示,但是我还是愿意重复一下。 ~~~ >>> city_code = {"suzhou":"0512", "tangshan":"0315", "hangzhou":"0571"} >>> " Suzhou is a beautiful city, its area code is %(suzhou)s" % city_code ' Suzhou is a beautiful city, its area code is 0512' ~~~ 这种写法是非常简洁,而且很有意思的。有人说它简直是酷。 其实,更酷还是下面的——模板 在做网页开发的时候,通常要用到模板,也就是你只需要写好HTML代码,然后将某些部位空出来,等着python后台提供相应的数据即可。当然,下面所演示的是玩具代码,基本没有什么使用价值,因为在真实的网站开发中,这样的姿势很少用上。但是,它绝非花拳绣腿,而是你能够明了其本质,至少了解到一种格式化方法的应用。 ~~~ >>> temp = "<html><head><title>%(lang)s<title><body><p>My name is %(name)s.</p></body></head></html>" >>> my = {"name":"qiwsir", "lang":"python"} >>> temp % my '<html><head><title>python<title><body><p>My name is qiwsir.</p></body></head></html>' ~~~ temp就是所谓的模板,在双引号所包裹的实质上是一段HTML代码。然后在dict中写好一些数据,按照模板的要求在相应位置显示对应的数据。 是不是一个很有意思的屠龙之技? > 什么是HTML? 下面是在《维基百科》上抄录的: > > 超文本标记语言(英文:HyperText Markup Language,HTML)是为「网页创建和其它可在网页浏览器中看到的信息」设计的一种标记语言。HTML被用来结构化信息——例如标题、段落和列表等等,也可用来在一定程度上描述文档的外观和语义。1982年由蒂姆·伯纳斯-李创建,由IETF用简化的SGML(标准通用标记语言)语法进行进一步发展的HTML,后来成为国际标准,由万维网联盟(W3C)维护。 > > HTML经过发展,现在已经到HTML5了。现在的HTML设计,更强调“响应式”设计,就是能够兼顾PC、手机和各种PAD的不同尺寸的显示器浏览。如果要开发一个网站,一定要做到“响应式”设计,否则就只能在PC上看,在手机上看就不得不左右移动。 ## [](https://github.com/qiwsir/StarterLearningPython/blob/master/116.md#知识)知识 什么是关联数组?以下解释来自[维基百科](http://zh.wikipedia.org/wiki/%E5%85%B3%E8%81%94%E6%95%B0%E7%BB%84) > 在计算机科学中,关联数组(英语:Associative Array),又称映射(Map)、字典(Dictionary)是一个抽象的数据结构,它包含着类似于(键,值)的有序对。一个关联数组中的有序对可以重复(如C++中的multimap)也可以不重复(如C++中的map)。 > > 这种数据结构包含以下几种常见的操作: > > > 1.向关联数组添加配对 2.从关联数组内删除配对 3.修改关联数组内的配对 4.根据已知的键寻找配对 > > 字典问题是设计一种能够具备关联数组特性的数据结构。解决字典问题的常用方法,是利用散列表,但有些情况下,也可以直接使用有地址的数组,或二叉树,和其他结构。 > > 许多程序设计语言内置基本的数据类型,提供对关联数组的支持。而Content-addressable memory则是硬件层面上实现对关联数组的支持。 什么是哈希表?关于哈希表的叙述比较多,这里仅仅截取了概念描述,更多的可以到[维基百科上阅读](http://zh.wikipedia.org/wiki/%E5%93%88%E5%B8%8C%E8%A1%A8)。 > 散列表(Hash table,也叫哈希表),是根据关键字(Key value)而直接访问在内存存储位置的数据结构。也就是说,它通过把键值通过一个函数的计算,映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。