1.10 字典

你现在还用字典吗?随着网络的发展,用字典的人越来越少了,不少人习惯于在网上搜索。在很久以前,我曾拥有一本小小的《新华字典》。

《新华字典》是中国第一部现代汉语字典,最早的名字叫《伍记小字典》,但未能编纂完成。自1953年,开始重编,其凡例完全采用《伍记小字典》。从1953年开始出版,经过反复修订,但是以1957年商务印书馆出版的《新华字典》作为第一版。由原新华辞书社编写,1956年并入中科院语言研究所(现中国社科院语言研究所)词典编辑室,新华字典由商务印书馆出版。历经几代上百名专家学者10余次大规模修订,重印200多次。成为迄今为止世界出版史上发行量最高的字典。

在这里讲到字典,不是为了回忆,而是提醒读者想想我们如何使用字典:先查索引,然后通过索引找到相应内容,不用从头开始一页一页地找,这种方法能够快捷地直达目标。

正是基于这种需要,Python中有了一种叫作dictionary的对象类型,翻译过来就是“字典”,用dict表示。

假设有一种需要,要存储城市和电话区号,苏州的区号是0512,唐山的是0315,北京的是011,上海的是012。用前面已经学习过的知识,可以这样来做:

  1. >>> citys = ["suzhou", "tangshan", "beijing", "shanghai"]
  2. >>> city_codes = ["0512", "0315", "011", "012"]

用一个列表来存储城市名称,然后用另外一个列表一一对应地保存区号。假如要输出苏州的区号,可以这样做:

  1. >>> print "{0} : {1}".format(citys[0], city_codes[0])
  2. suzhou : 0512

在city_codes中表示区号的元素没有用整数型,而是使用了字符串类型,你知道为什么吗?如果用整数,就是这样的:

  1. >>> suzhou_code = 0512
  2. >>> print suzhou_code
  3. 330

怎么会这样?原来,在Python中如果按照上面那样做,0512被认为是一个八进制的数,用print打印的时候,将它转换为了十进制输出。关于进制转换问题,可以在网上搜索一下有关资料,此处不详述。一般是用几个内建函数实现:int(),bin(),oct(),hex()。

用两个列表分别来存储城市和区号,似乎能够解决问题。但是,这不是最好的选择,因为Python还提供了另外一种方案,那就是“字典”。

1.10.1 创建字典

方法1:

创建一个空的字典,然后可以加入东西。

  1. >>> mydict = {}
  2. >>> mydict
  3. {}

不要小看“空”,在编程中,“空”是很重要。

当然可以创建一个不空的字典:

  1. >>> person = {"name":"qiwsir", "site":"qiwsir.github.io", "language":"python"}
  2. >>> person
  3. {'name': 'qiwsir', 'language': 'python', 'site': 'qiwsir.github.io'}

"name":"qiwsir"有一个优雅的名字:键值对。前面的name叫做键(key),后面的qiwsir是前面的键所对应的值(value)。在一个字典中,键是唯一的,不能重复。值则对应于键,且值可以重复。键值之间用英文的冒号,每一对键值之间用英文的逗号隔开。

  1. >>> person['name2']="qiwsir" #增加键值对的方法
  2. >>> person
  3. {'name2': 'qiwsir', 'name': 'qiwsir', 'language': 'python', 'site': 'qiwsir.github.io'}

用这样的方法可以向一个字典中增加“键值对”,那么,增加了值之后,那个字典对象还是原来的内存地址吗?即也要探讨字典是否能原地修改?(列表可以,因为列表是可变的;字符串和元组都不行,因为它们是不可变的)。

  1. >>> ad = {}
  2. >>> id(ad)
  3. 3072770636L
  4. >>> ad["name"] = "qiwsir"
  5. >>> ad
  6. {'name': 'qiwsir'}
  7. >>> id(ad)
  8. 3072770636L

实验表明,字典可以原地修改,即它是可变的。

方法2:

利用元组建构字典,方法如下:

  1. >>> name = (["first", "Google"], ["second", "Yahoo"])
  2. >>> website = dict(name)
  3. >>> website
  4. {'second': 'Yahoo', 'first': 'Google'}

或者用这样的方法:

  1. >>> ad = dict(name="qiwsir", age=42)
  2. >>> ad
  3. {'age': 42, 'name': 'qiwsir'}

方法3:

这个方法,跟以上方法的不同在于使用fromkeys:

  1. >>> website = {}.fromkeys(("third","forth"),"facebook")
  2. >>> website
  3. {'forth': 'facebook', 'third': 'facebook'}

特别注意,字典中的“键”,必须是不可变对象;“值”可以是任意类型的对象。

  1. >>> dd = {(1,2):1}
  2. >>> dd
  3. {(1, 2): 1}
  4. >>> dd = {[1,2]:1}
  5. Traceback (most recent call last):
  6. File "<stdin>", line 1, in <module>
  7. TypeError: unhashable type: 'list'

1.10.2 访问字典的值

字典类型的对象是以键值对的形式存储数据的,所以,只要知道键,就能得到值,这在本质上就是一种映射关系。

映射,就好比“物体”和“影子”的关系,“形影相吊”,两者之间是映射关系。此外,映射也是一个严格的数学概念:A是非空集合。A到B的映射是指:A中每个元素都对应到B中的某个元素。

既然是映射,就可以通过字典的“键”找到相应的“值”。

  1. >>> person
  2. {'name2': 'qiwsir', 'name': 'qiwsir', 'language': 'python', 'site': 'qiwsir.github.io'}
  3. >>> person['name']
  4. 'qiwsir'
  5. >>> person['language']
  6. 'python'

“键”很关键,因为通过“键”能够增加“值”,通过“键”能够改变“值”,通过“键”也能够访问到“值”。

本小节开头的城市和区号的关系,也可以用字典来存储和读取。

  1. >>> city_code = {"suzhou":"0512", "tangshan":"0315", "beijing":"011", "shanghai":"012"}
  2. >>> print city_code["suzhou"]

既然字典是键值对的映射,就不用考虑所谓“排序”问题了,只要通过键就能找到值,至于这个键值对的位置在哪里就不用考虑了。比如,刚才建立的city_code。

  1. city_code
  2. >>> city_code
  3. {'suzhou': '0512', 'beijing': '011', 'shanghai': '012', 'tangshan': '0315'}

虽然这里显示的和刚刚赋值的时候顺序有别,但是不影响读取其中的值。

在列表中,通过索引值可以得到某个元素。那么在字典中有索引吗?当然没有,因为它没有顺序,又哪里来的索引呢?所以,在字典中就不要什么索引和切片了。

字典中的这类以“键值对”的映射方式存储数据是一种非常高效的方法,比如要读取值的时候,如果用列表,Python需要从头开始读,直到找到指定的那个索引值。但是,在字典中是通过“键”来得到值,要高效得多。正是这个特点,“键值对”这样的形式可以用来存储大规模的数据,因为检索快捷,规模越大越明显。所以,mongdb这种非关系型数据库在大数据方面比较流行。

1.10.3 基本操作

列举字典的基本操作:

  • len(d),返回字典(d)中的键值对的数量。
  • d[key],返回字典(d)中的键(key)的值。
  • d[key]=value,将值(value)赋给字典(d)中的键(key)。
  • del d[key],删除字典(d)的键(key)项(将该键值对删除)。
  • key in d,检查字典(d)中是否含有键为key的项。依次进行演示。
  1. >>> city_code
  2. {'suzhou': '0512', 'beijing': '011', 'shanghai': '012', 'tangshan': '0315'}
  3. >>> len(city_code)
  4. 4

以city_code为操作对象,len(city_code)的值是4,表明有四组键值对,也可以说是四项。如果增加项目,则:

  1. >>> city_code["nanjing"] = "025"
  2. >>> city_code
  3. {'suzhou': '0512', 'beijing': '011', 'shanghai': '012', 'tangshan': '0315', 'nanjing': '025'}

突然发现北京的区号写错了。可以这样修改,这进一步说明字典是可变的。

  1. >>> city_code["beijing"] = "010"
  2. >>> city_code
  3. {'suzhou': '0512', 'beijing': '010', 'shanghai': '012', 'tangshan': '0315', 'nanjing': '025'}

不仅北京的写错了,上海的也写错了,干脆删除,使用del,将那一项全部删掉。

  1. >>> city_code["shanghai"]
  2. '012'
  3. >>> del city_code["shanghai"]
  4. >>> city_code["shanghai"]
  5. Traceback (most recent call last):
  6. File "<stdin>", line 1, in <module>
  7. KeyError: 'shanghai'

"shanghai"的那个键值对项已经删除了,所以不能找到,用in来看看,返回的是False。

  1. >>> "shanghai" in city_code
  2. False

1.10.4 字符串格式化输出

字符串格式化输出问题是前面已经存在的内容,这里再次提到是因为用字典也可以实现格式化字符串的目的。

  1. >>> city_code = {"suzhou":"0512", "tangshan":"0315", "hangzhou":"0571"}
  2. >>> " Suzhou is a beautiful city, its area code is %(suzhou)s" % city_code
  3. ' Suzhou is a beautiful city, its area code is 0512'

这种写法非常简洁,而且很有意思,有人说它很酷。

其实,更酷的是下面的——模板

在做网页开发的时候通常要用到模板,你只需要写好HTML代码,然后将某些部位空出来,等着Python后台提供相应的数据即可。当然,下面所演示的是玩具代码,基本没有什么实用价值,因为在真实的网站开发中,这样的知识很少用。但是,它绝非花拳绣腿,而是你能够明了其本质,至少了解到一种格式化方法的应用。

  1. >>> temp = "<html><head><title>%(lang)s<title><body><p>My name is %(name)s.</p> </body></head></html>"
  2. >>> my = {"name":"qiwsir", "lang":"python"}
  3. >>> temp % my
  4. '<html><head><title>python<title><body><p>My name is qiwsir.</p></body></head> </html>'

temp就是所谓的模板,双引号所包裹的实质上是一段HTML代码。然后在字典中写好一些数据,按照模板的要求在相应位置显示对应的数据。

是不是一个很有意思的屠龙之技?

1.10.5 相关概念

以下内容是跟字典有关联的基本知识,从维基百科上抄录下来,供读者参考使用。

1.关联数组

在计算机科学中,关联数组(英语:Associative Array)又称映射(Map)、字典(Dictionary)是一个抽象的数据结构,它包含着类似于“键值”的有序对。一个关联数组中的有序对可以重复(如C++中的multimap)也可以不重复(如C++中的map)。

这种数据结构包含以下几种常见的操作:

(1)向关联数组添加配对。

(2)从关联数组内删除配对。

(3)修改关联数组内的配对。

(4)根据已知的键寻找配对。

字典问题是设计一种能够具备关联数组特性的数据结构。解决字典问题的常用方法是利用散列表,但有些情况也可以直接使用有地址的数组、二叉树,或者其他结构。

许多程序设计语言内置基本的数据类型,提供对关联数组的支持。而Content-addressable memory则是硬件层面上实现对关联数组的支持。

2.散列表

散列表(Hash table,也叫哈希表),是根据关键字(Key value)而直接访问在内存存储位置的数据结构。即把键值通过一个函数的计算,映射到表中一个位置来访问记录,加快了查找速度。这个映射函数称作散列函数,存放记录的数组称作散列表。

1.10.6 字典的函数

跟前面所讲述的其他对象类似,字典也有一些函数。通过这些函数,能够实现对字典的操作。

1.copy和deepcopy

拷贝是copy的音译,标准的汉语翻译是“复制”。

在一般的理解中,copy就是将原来的东西再做一份。但是,在Python里面(乃至于很多编程语言中),copy可不是那么简单的。

  1. >>> a = 5
  2. >>> b = a
  3. >>> b
  4. 5

这样做是不是就得到了两个5了呢?表面上看似乎是,但是,不要忘记在前面反复提到的:对象有类型,变量无类型,正是因为这句话,变量其实是一个标签。不妨请出法宝:id(),专门查看内存中的对象编号。

  1. >>> id(a)
  2. 139774080
  3. >>> id(b)
  4. 139774080

果然,并没有两个5,就一个,只不过是贴了两张标签而已。这种现象普遍存在于Python的多种数据类型中。其他的就不演示了,就仅看看dict类型。

  1. >>> ad = {"name":"qiwsir", "lang":"python"}
  2. >>> bd = ad
  3. >>> bd
  4. {'lang': 'python', 'name': 'qiwsir'}
  5. >>> id(ad)
  6. 3072239652L
  7. >>> id(bd)
  8. 3072239652L

又是一个对象贴了两个标签,这是用赋值的方式实现的所谓“假装拷贝”。那么如果用copy的方法呢?

  1. >>> cd = ad.copy()
  2. >>> cd
  3. {'lang': 'python', 'name': 'qiwsir'}
  4. >>> id(cd)
  5. 3072239788L

这次得到的cd跟原来的ad是不同的,它在内存中另辟了一个空间。如果我尝试修改cd,应该对原来的ad不会造成任何影响。

  1. >>> cd["name"] = "itdiffer.com"
  2. >>> cd
  3. {'lang': 'python', 'name': 'itdiffer.com'}
  4. >>> ad
  5. {'lang': 'python', 'name': 'qiwsir'}

真的跟推理一模一样。所以,只要理解了“变量”是对象的标签,对象有类型而变量无类型,就能正确推断出Python能够提供的结果。刚才已经看到了,bd和ad引用了同一个内存对象。

  1. >>> bd
  2. {'lang': 'python', 'name': 'qiwsir'}
  3. >>> bd["name"] = "laoqi"
  4. >>> ad
  5. {'lang': 'python', 'name': 'laoqi'}
  6. >>> bd
  7. {'lang': 'python', 'name': 'laoqi'}

修改了bd所对应的“对象”,ad的“对象”也变了。

然而,事情并没有那么简单,下面看仔细一点,否则就迷茫了。

  1. >>> x = {"name":"qiwsir", "lang":["python", "java", "c"]}
  2. >>> y = x.copy()
  3. >>> y
  4. {'lang': ['python', 'java', 'c'], 'name': 'qiwsir'}
  5. >>> id(x)
  6. 3072241012L
  7. >>> id(y)
  8. 3072241284L

y是从x拷贝过来的,两个在内存中是不同的对象。

  1. >>> y["lang"].remove("c")

在y所对应的字典对象中,键"lang"的值是一个列表,为['python','java','c'],这里用remove()删除其中的一个元素"c"。删除之后,这个列表变为:['python','java']。

  1. >>> y
  2. {'lang': ['python', 'java'], 'name': 'qiwsir'}

那么,x所对应的字典中,这个列表变化了吗?应该没有变化,因为按照前面所讲的,它是另外一个对象,两个互不干扰。

  1. >>> x
  2. {'lang': ['python', 'java'], 'name': 'qiwsir'}

仔细观察,是不是有点出乎意料呢?我没有作弊哦。如果不信,就按照操作自己在交互模式中试试,是不是也能够得到这个结果呢?这是为什么?

但是,如果要操作另外一个键值对:

  1. >>> y["name"] = "laoqi"
  2. >>> y
  3. {'lang': ['python', 'java'], 'name': 'laoqi'}
  4. >>> x
  5. {'lang': ['python', 'java'], 'name': 'qiwsir'}

前面所说的原理是有效的,为什么当值是列表的时候就不奏效了呢?

要破解这个迷局还得用id():

  1. >>> id(x)
  2. 3072241012L
  3. >>> id(y)
  4. 3072241284L

x和y对应着两个不同的对象,的确如此。但这个对象(字典)是由两个键值对组成的,其中一个键的值是列表。

  1. >>> id(x["lang"])
  2. 3072243276L
  3. >>> id(y["lang"])
  4. 3072243276L

发现了这样一个事实:列表是同一个对象。

但是,作为字符串为值的那个键值对分属不同的对象。

  1. >>> id(x["name"])
  2. 3072245184L
  3. >>> id(y["name"])
  4. 3072245408L

这个事实就说明了为什么修改一个列表,另外一个也跟着修改;而修改一个字符串,另外一个不跟随的原因了。

但是,似乎还没有解开深层的原因。深层的原因与Python存储的对象类型(在不少地方也用“数据类型”的说法,其实两者是一样的,“对象”和“数据”在Python中等同,不用区分)特点有关,Python只存储基本类型的数据,比如int、str,对于不是基础类型的,比如字典的值是列表,Python不会在被复制的那个对象中重新存储,而是用引用的方式,指向原来的值。通俗地说,Python在所执行的复制动作中,如果是基本类型的数据,就在内存中重新建个窝,如果不是基本类型的,就不新建窝了,而是用标签引用原来的窝。即如果比较简单,随便建立新窝即可;但是,如果对象太复杂了,就别费劲了,还是引用一下原来的省事。

所以,把用copy()实现的拷贝称之为“浅拷贝”(不仅Python,很多语言都有“浅拷贝”。顾名思义,没有解决深层次问题。言外之意,还有能够解决深层次问题的方法)。

与“浅拷贝”对应,在Python中,还有一个“深拷贝”(deep copy)。不过,要用import导入一个模块。

  1. >>> import copy
  2. >>> z = copy.deepcopy(x)
  3. >>> z
  4. {'lang': ['python', 'java'], 'name': 'qiwsir'}

用copy.deepcopy()深拷贝了一个新的副本,用id()来勘察一番:

  1. >>> id(x["lang"])
  2. 3072243276L
  3. >>> id(z["lang"])
  4. 3072245068L

果然是另外一个“窝”,不是引用了。如果按照这个结果,修改其中一个列表中的元素,应该就不影响另外一个了。

  1. >>> x
  2. {'lang': ['python', 'java'], 'name': 'qiwsir'}
  3. >>> x["lang"].remove("java")
  4. >>> x
  5. {'lang': ['python'], 'name': 'qiwsir'}
  6. >>> z
  7. {'lang': ['python', 'java'], 'name': 'qiwsir'}

果然如此,再试试才过瘾呀。

  1. >>> x["lang"].append("c++")
  2. >>> x
  3. {'lang': ['python', 'c++'], 'name': 'qiwsir'}

这就是所谓的浅拷贝和深拷贝。

2.clear

在交互模式中,用help是一个很好的习惯。

  1. >>> help(dict.clear)
  2. clear(...)
  3. D.clear() -> None. Remove all items from D.

这是一个清空字典中所有元素的操作。

  1. >>> a = {"name":"qiwsir"}
  2. >>> a.clear()
  3. >>> a
  4. {}

clear的含义是将字典清空,得到的是“空”字典。它和del有着很大的区别,del是将字典删除,内存中就没有它了,并不是为“空”(“空”和无是有区别的,至少在编程语言中有区别。在其他方面也有区别,比如“色即是空”,不能是“无”吧)。

  1. >>> del a
  2. >>> a
  3. Traceback (most recent call last):
  4. File "<stdin>", line 1, in <module>
  5. NameError: name 'a' is not defined

果然删除了。

另外,如果要清空一个字典,还能够使用a={}这种方法,但这种方法的本质是将变量a转向了{}这个对象,那么原来的呢?原来的成为了断线的风筝。这样的东西在Python中称之为垃圾,而且Python能够自动将这样的垃圾回收。读者就不用关心它了,反正Python会处理的。

3.get和setdefault

get的含义是:

  1. get(...)
  2. D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.

注意,在这个说明中,“if k in D”就返回其值。

  1. >>> d
  2. {'lang': 'python'}
  3. >>> d.get("lang")
  4. 'python'

dict.get()就是要得到字典中某个键的值,只是它没有那么“严厉”罢了。因为类似获得字典中键值的方法,如d['lang']就能得到对应的值"python",可是,如果要获取的键不存在:

  1. >>> print d.get("name")
  2. None
  3.  
  4. >>> d["name"]
  5. Traceback (most recent call last):
  6. File "<stdin>", line 1, in <module>
  7. KeyError: 'name'

这就是dict.get()和dict['key']的区别。

如果键不在字典中,会返回None,这是一种情况,另外还可以这样:

  1. >>> d = {"lang":"python"}
  2. >>> newd = d.get("name",'qiwsir')
  3. >>> newd
  4. 'qiwsir'
  5. >>> d
  6. {'lang': 'python'}

以d.get("name",'qiwsir')的方式,如果不能得到键"name"的值,就返回后面指定的值"qiwsir"。这就是文档中D[k]if k in D,else d.的含义,这样做并没有影响原来的字典。

另外一个跟get在功能上有相似地方的是D.setdefault(k),其含义是:

  1. setdefault(...)
  2. D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D

首先,它要执行D.get(k,d)就跟前面一样了,然后,进一步执行另外一个操作,如果键k不在字典中,就在字典中增加这个键值对。当然,如果有就没有必要执行这一步了。

  1. >>> d
  2. {'lang': 'python'}
  3. >>> d.setdefault("lang")
  4. 'python'

在字典中,有"lang"这个键,就返回它的值。

  1. >>> d.setdefault("name","qiwsir")
  2. 'qiwsir'
  3. >>> d
  4. {'lang': 'python', 'name': 'qiwsir'}

在字典中没有"name"这个键,于是返回d.setdefault("name","qiwsir")指定的值"qiwsir",并且将键值对'name':"qiwsir"添加到原来的字典中。

如果这样操作:

  1. >>> d.setdefault("web")

什么也没有返回吗?不是,返回了,只不过没有显示出来,如果你用print就能看到了。因为这里返回的是一个None,不妨查看一下那个字典:

  1. >>> d
  2. {'lang': 'python', 'web': None, 'name': 'qiwsir'}

键"web"的值成为了None。

4.items/iteritems,keys/iterkeys,values/itervalues

这个标题中列出的是三组函数,并且这三组有相似的地方。在这里详细讲述一下第一组,其余两组,我想凭借读者的聪明智慧是不在话下的。

  1. >>> help(dict.items)
  2. items(...)
  3. D.items() -> list of D's (key, value) pairs, as 2-tuples

这种获取帮助信息的方法是惯用的伎俩了,希望读者能熟悉,并在自己的代码生涯中常用。D.items()能够得到一个关于字典的列表,列表中的元素是由字典中的键和值组成的元组。例如:

  1. >>> dd = {"name":"qiwsir", "lang":"python", "web":"www.itdiffer.com"}
  2. >>> dd_kv = dd.items()
  3. >>> dd_kv
  4. [('lang', 'python'), ('web', 'www.itdiffer.com'), ('name', 'qiwsir')]

显然,是有返回值的。这个操作在后面要讲到的循环中将有很大的作用。

跟items类似的是iteritems,这个词的特点是由iter和items拼接而成的,后部分items就不用说了,肯定是在告诉我们,得到的结果跟D.items()的结果类似。还有一个iter是什么意思?前面我提到了一个词“iterable”,它的含义是“可迭代的”,这里的iter是指名词iterator的前部分,意思是“迭代器”。合起来,"iteritems"的含义就是:

  1. iteritems(...)
  2. D.iteritems() -> an iterator over the (key, value) items of D

你看,学习Python不是什么难事,只要充分使用帮助文档就好了。这里告诉我们,得到的是一个“迭代器”(关于什么是迭代器,以及相关的内容,后续会详细讲述),这个迭代器是关于“D.items()”的。看个例子就明白了。

  1. >>> dd
  2. {'lang': 'python', 'web': 'www.itdiffer.com', 'name': 'qiwsir'}
  3. >>> dd_iter = dd.iteritems()
  4. >>> type(dd_iter)
  5. <type 'dictionary-itemiterator'>
  6. >>> dd_iter
  7. <dictionary-itemiterator object at 0xb72b9a2c>
  8. >>> list(dd_iter)
  9. [('lang', 'python'), ('web', 'www.itdiffer.com'), ('name', 'qiwsir')]

得到的dd_iter是一个'dictionary-itemiterator'类型,不过这种迭代器类型的数据不能直接输出,必须用list()转换一下,才能看到里面的真面目。

另外两组含义跟这个相似,只不过是得到key或者value。下面仅列举一下例子,具体内容,读者可以自行在交互模式中看文档。

  1. >>> dd
  2. {'lang': 'python', 'web': 'www.itdiffer.com', 'name': 'qiwsir'}
  3. >>> dd.keys()
  4. ['lang', 'web', 'name']
  5. >>> dd.values()
  6. ['python', 'www.itdiffer.com', 'qiwsir']

这里先交代一句,如果要实现对“键值”对或者“键”或者“值”的循环,用迭代器的效率会高一些。对这句话的理解,继续阅读本书就能找到解释。

5.pop和popitem

还记得删除列表中元素的函数是哪个吗?pop和remove,这两个的区别在于:list.remove(x)用来删除指定的元素,而list.pop([i])用于删除指定索引的元素,如果不提供索引值,就默认删除最后一个。

在字典中,也有删除键值对的函数。

  1. pop(...)
  2. D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
  3. If key is not found, d is returned if given, otherwise KeyError is raised

D.pop(k[,d])是以字典的键为参数,删除指定键的键值对,当然,如果输入对应的值也可以,那个是可选的。

  1. >>> dd
  2. {'lang': 'python', 'web': 'www.itdiffer.com', 'name': 'qiwsir'}
  3. >>> dd.pop("name")
  4. 'qiwsir'

删除指定键"name",返回了其值"qiwsir"。这样,在原字典中,“'name':'qiwsir'”这个键值对就被删除了。

  1. >>> dd
  2. {'lang': 'python', 'web': 'www.itdiffer.com'}

值得注意的是,pop函数中的参数是不能省略的,这跟列表中的pop有所不同。

  1. >>> dd.pop()
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. TypeError: pop expected at least 1 arguments, got 0

如果要删除字典中没有的键值对,也会报错。

  1. >>> dd.pop("name")
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. KeyError: 'name'

有意思的是D.popitem()跟list.pop()有相似之处,不用写参数(list.pop()可以不写参数),但是,D.popitem()不是删除最后一个,dict没有顺序,也就没有最后和最先了,它是随机删除一个,并将所删除的返回。

  1. popitem(...)
  2. D.popitem() -> (k, v), remove and return some (key, value) pair as a
  3. 2-tuple; but raise KeyError if D is empty.

如果字典是空的,就要报错了

  1. >>> dd
  2. {'lang': 'python', 'web': 'www.itdiffer.com'}
  3. >>> dd.popitem()
  4. ('lang', 'python')
  5. >>> dd
  6. {'web': 'www.itdiffer.com'}

成功地删除了一对,注意是随机的,不是删除前面显示的最后一个,你做同样的操作,或许删除的对象跟我删除的不一样。并且返回了删除的内容,返回值是元组类型,且其元素为所删除的键和值。

  1. >>> dd.popitems()
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. AttributeError: 'dict' object has no attribute 'popitems'

错了?注意看提示信息,果然错了。注意是popitem,不要多了s,前面的D.items()中包含s,是复数形式,说明它能够返回多个结果(多个元组组成的列表),而在D.popitem()中,一次只能随机删除一对键值对,并以一个元组的形式返回,所以,要用单数形式,不能用复数形式了。

  1. >>> dd.popitem()
  2. ('web', 'www.itdiffer.com')
  3. >>> dd
  4. {}

都删了,字典成空的了。如果再删,会怎么样?

  1. >>> dd.popitem()
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. KeyError: 'popitem(): dictionary is empty'

报错信息中明确告知,字典已经是空的了,没有能删的东西了。

6.update

update(),看名字就猜测到一二了,是不是更新字典内容呢?的确是。

  1. update(...)
  2. D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
  3. If E present and has a .keys() method, does: for k in E: D[k] = E[k]
  4. If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
  5. In either case, this is followed by: for k in F: D[k] = F[k]

看样子这个函数有点复杂,不要着急,通过实验可以一点一点鼓捣明白。

首先,这个函数返回值是None,它的作用就是更新字典。其参数可以是字典或者某种可迭代的对象。

  1. >>> d1 = {"lang":"python"}
  2. >>> d2 = {"song":"I dreamed a dream"}
  3. >>> d1.update(d2)
  4. >>> d1
  5. {'lang': 'python', 'song': 'I dreamed a dream'}
  6. >>> d2
  7. {'song': 'I dreamed a dream'}

这样就把字典d2更新纳入了d1那个字典,于是d1中就多了一些内容,因为把d2的内容包含进来了。当然d2还存在,并没有受到影响。

还可以用下面的方法更新:

  1. >>> d2
  2. {'song': 'I dreamed a dream'}
  3. >>> d2.update([("name","qiwsir"), ("web","itdiffer.com")])
  4. >>> d2
  5. {'web': 'itdiffer.com', 'name': 'qiwsir', 'song': 'I dreamed a dream'}

列表内的元组是键值对。

7.has_key

这个函数的功能是判断字典中是否存在某个键。

  1. has_key(...)
  2. D.has_key(k) -> True if D has a key k, else False

跟前一节中遇到的k in D类似。

  1. >>> d2
  2. {'web': 'itdiffer.com', 'name': 'qiwsir', 'song': 'I dreamed a dream'}
  3. >>> d2.has_key("web")
  4. True
  5. >>> "web" in d2

关于dict的函数似乎不少。不用着急,也不用担心记不住,因为根本不需要记忆,只要会用搜索即可。