本章中,你将学到一种通过名字引用值的数据结构.这种结构类型称为映射(mapping).字典是 Python 中唯一内建的映射类型.字典中的值并没有特殊的顺序,但是都存储在一个特定的键里.键可以是数字、字符串甚至是元组.
字典的使用
查找某个特定的词语(键),从而找到它的定义(值).
创建和使用字典
字典通过下面的方式创建:
>>> phonebook = {'Alice': '2134', 'Beth': '9102', 'Cecil': '3258'}
>>> phonebook['Beth']
'9102'
字典由多个键及与其对应的值构成的对组成(我们也把键/值对称为项).每个键和它的值之间用冒号(:)隔开,项之间用逗号(,)隔开,而整个字典是由一对大括号括起来.空字典(不包括任何项)由两个大括号组成,像这样:{}.
注意:字典中的键是唯一的(其他类型的映射也是如此),而值并不唯一.
dict 函数
dict 函数根本不是真正的函数.它是个类型,就像 list, tuple 和 str 一样.
可以使用 dict 函数,通过其他映射(比如其他字典)或者(键,值)这样的序列对建立字典.
>>> items = [('name', 'Gumby'), ('age', 42)]
>>> d = dict(items)
>>> d
{'age': 42, 'name': 'Gumby'}
>>> d['name']
'Gumby'
dict 函数也可以通过关键字参数来创建字典,如下例所示:
>>> d = dict(name='Gumby', age=42)
>>> d
{'age': 42, 'name': 'Gumby'}
这可能是 dict 函数最有用的功能了,但是还能以映射作为 dict 函数的参数,以建立其项与映射相同的字典(如果不带任何参数,则 dict 函数返回一个新的空字典,就像 list, tuple 以及 str 等函数一样).如果另一个映射也是字典(毕竟这是唯一内建的映射类型),也可以使用本章稍后讲到的字典方法 copy.
基本字典操作
字典的基本行为在很多方面与序列类似:
- len(d) 返回 d 中项(键值对)的数量;
- d[k] 返回关联到键 k 上的值;
- d[k]=v 将值 v 关联到键 k 上;
- del d[k] 删除键为 k 的项;
- k in d 检查 d 中是否有含有键为 k 的项.
尽管字典和列表有很多特性相同,单也有下面一些重要的区别. - 键类型:字典的键不一定为整型数据(但也可能是),也可能是其他不可变类型,比如浮点型(实型)、字符串或者元组.
- 自动添加:即使那个键起初在字典中并不存在,也可以为它分配一个值,这样字典就会建立新的项.而(在不使用 append 方法或者其他类似操作的情况下)不能将值关联到列表范围之外的索引上.
- 成员资格:表达式 k in d (d 为字典)查找的是键,而不是值.表达式 v in l (l 为列表)则用来查找值,
提示:在字典中检查的成员资格比在列表中检查值的成员资格更高效,数据结构的规模越大,两者的效率差距越明显.
第一点——键可以为任何不可变类型——是字典最强大的地方.第二点也很重要:
>>> x = []
>>> x[42] = 'Foobar'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
>>> x = {}
>>> x[42] = 'Foobar'
>>> x
{42: 'Foobar'}
字典的格式化字符串
在每个转换说明符中的 % 字符后面,可以加上(用圆括号括起来的)键,后面再跟上其他说明元素.
>>> phonebook = {'Beth': '9102', 'Alice': '2341', 'Cecil': '3258'}
>>> "Cecil's phone number is %(Cecil)s." % phonebook
"Cecil's phone number is 3258."
>>> "Beth: %(Beth)s, Alice: %(Alice)s, Cecil: %(Cecil)s" % phonebook
'Beth: 9102, Alice: 2341, Cecil: 3258'
注意:string.Template类(第三章提到过)对于这类应用也是非常有用的.
字典方法
clear 方法清除字典中所有的项.这是个原地操作(类似于 list.sort),所以无返回值(或者说返回 None).
>>> d = {}
>>> d['name'] = 'Gumby'
>>> d['age'] = 42
>>> d
{'age': 42, 'name': 'Gumby'}
>>> return_value = d.clear()
>>> d
{}
>>> print return_value
None
这个方法的用处,第一种情况:
>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x = {}
>>> y
{'key': 'value'}
第二种情况:
>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x.clear()
>>> y
{}
>>> x
{}
两种情况中,x 和 y 最初对应同一个字典.情况 1 中,通过将 x 关联到一个新的空字典来"清空"它,这对 y 一点影响也没有,它还关联到原先的字典.如果想清空原始字典中的所有的元素,必须使用 clear 方法.
copy 方法返回一个具有相同键-值对的新字典(这个方法实现的是浅复制),因为值本身就是相同的,而不是副本).
>>> x = {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> y = x.copy()
>>> y['username'] = 'mlh'
>>> y['machines'].remove('bar')
>>> y
{'username': 'mlh', 'machines': ['foo', 'baz']}
>>> x
{'username': 'admin', 'machines': ['foo', 'baz']}
可以看到,当在副本中替换值的时候,原始字典不受影响,但是,如果修改了某个值(原地修改,而不是替换),原始的字典也会改变,因为同样的值也存储在原字典中(就像上面例子中的 “machines” 列表一样).
避免这个问题的一种方法就是使用深复制,复制其包含所有的值.可以使用 copy 模块的 deepcopy 函数来完成操作:
>>> from copy import deepcopy
>>> d = {}
>>> d['names'] = ['Alfred', 'Bertrand']
>>> c = d.copy()
>>> dc = deepcopy(d)
>>> d['names'].append('Clive')
>>> c
{'names': ['Alfred', 'Bertrand', 'Clive']}
>>> dc
{'names': ['Alfred', 'Bertrand']}
fromkeys 方法使用给定的键建立新的字典,每个键默认对应的值为 None:
>>> {}.fromkeys(['name', 'age'])
{'age': None, 'name': None}
上例首先建立了一个空词典,然后建立另外一个新词典.也可以直接在所有字典的类型 dict 上面调用方法:
>>> dict.fromkeys(['name', 'age'])
{'age': None, 'name': None}
也可以自己提供默认值:
>>> dict.fromkeys(['name', 'age'], 'unkown')
{'age': 'unkown', 'name': 'unkown'}
get 方法是个更宽松的访问字典项的方法.
>>> d = {}
>>> print d['name']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'name'
>>> print d.get('name')
None
当 get 访问一个不存在的键时,没有任何异常,而得到了 None 值.还可以自定义"默认"值,替换 None:
>>> print d.get('name', 'N/A')
N/A
>>> d['name'] = 'Eric'
>>> d.get('name')
'Eric'
has_key 方法可以检查字典中是否含有给出的键.表达式 d.has_key(k) 相当于表达式 k in d. Python 3.0 中不包括这个函数.
>>> d = {}
>>> d.has_key('name')
False
>>> d['name'] = 'Eric'
>>> d.has_key('name')
True
items 方法将所有的字典项以列表方式返回,这些列表项中的每一项都来自于(键,值).但是项在返回时并没有特殊的顺序.
>>> d = {'title': 'python web site', 'url': 'http://www.python.org', 'spam': 0}
>>> d.items()
[('url', 'http://www.python.org'), ('spam', 0), ('title', 'python web site')]
iteritems 方法的作用大致相同,但是会返回一个迭代器对象而不是列表:
>>> it = d.iteritems()
>>> it
<dictionary-itemiterator object at 0x7ff9a4b703c0>
>>> list(it) # Convert the iterator to a list
[('url', 'http://www.python.org'), ('spam', 0), ('title', 'python web site')]
在很多情况下使用 iteritems 更高效(尤其是想要迭代结果的情况下).
keys 方法将字典中的键以列表形式返回:
>>> d = {'a': 1, 'b': 2}
>>> d
{'a': 1, 'b': 2}
>>> d.keys()
['a', 'b']
iterkeys 返回针对键的迭代器:
>>> it = d.iterkeys()
>>> it
<dictionary-keyiterator object at 0x7ff9a4b70418>
>>> list(it)
['a', 'b']
pop 方法用来获得对应于给定键的值,然后将这个键-值对从字典中移除:
>>> list(it)
['a', 'b']
>>>
>>> d = {'x': 1, 'y': 2}
>>> d.pop('x')
1
>>> d
{'y': 2}
popitem 方法类似于 list.pop,后者会弹出列表的最后一个元素.但不同的是,popitem 弹出随机的项,因为字典并没有"最后的元素"或者其他有关顺序的概念.若想一个接一个地移除并处理项,这个方法就非常有效了(因为不用首先获取键的列表).
>>> d = {'url': 'http://www.python.org', 'spam': 0, 'title': 'python web site'}
>>> d
{'url': 'http://www.python.org', 'title': 'python web site', 'spam': 0}
>>> d.popitem()
('url', 'http://www.python.org')
>>> d
{'title': 'python web site', 'spam': 0}
因为字典是无序的,所以没有 append 方法.
setdefault 方法在某种程度上类似于 get 方法,就是能够活得与给定键相关联的值,除此之外,setdefault 还能在字典中不含有给定键的情况下设定相应的键值.
>>> d = {}
>>> d.setdefault('name', 'N/A')
'N/A'
>>> d
{'name': 'N/A'}
>>> d['name'] = 'Gumby'
>>> d.setdefault('name', 'N/A')
'Gumby'
>>> d
{'name': 'Gumby'}
可以看到,当键不存在的时候,setdefault 返回默认值并且相应地更新字典.如果键存在,那么就返回与其对应的值,但不改变字典.默认值是可选的,这点和 get 一样.如果不设定,会默认使用 None.
>>> d = {}
>>> print d.setdefault('name')
None
>>> d
{'name': None}
update 方法可以利用一个字典项更新另外一个字典:
>>> d = {
... 'title': 'python web site',
... 'url': 'http://www.python.org',
... 'changed': 'Mar 14 22:09:15 MET 2008'
... }
>>> x = {'title': 'Python Language Website'}
>>> d.update(x)
>>> d
{'url': 'http://www.python.org', 'changed': 'Mar 14 22:09:15 MET 2008', 'title': 'Python Language Website'}
提供的字典中的项会被添加到旧的字典中,若有相同的键则会进行覆盖.
update 方法可以使用与调用 dict 函数(或者类型构造函数)同样的方式进行调用.这就意味着 update 可以和映射、拥有(键,值)对的队列(或者其他可迭代的对象)以及关键字参数一起调用.
values 方法以列表的形式返回字典中的值(itervalues 返回值的迭代器).与返回键的列表不同的是,返回值的列表中可以包含重复的元素:
>>> d = {}
>>> d[1] = 1
>>> d[2] = 2
>>> d[3] = 3
>>> d[4] = 1
>>> d.values()
[1, 2, 3, 1]
>>>
>>> it = d.itervalues()
>>> it
<dictionary-valueiterator object at 0x7f0f7a7c9520>
>>> list(it)
[1, 2, 3, 1]