/*鼠标点击特效*/

当索引行不通时

当索引行不通时

本章介绍一种可通过名称来访问其各个值的数据结构。这种数据结构称为映射(mapping)。字典是Python中唯一
的内置映射类型,其中的值不按顺序排列,而是存储在键下。键可能是数、字符串或元组。

4.1 字典的用途

字典(日常生活中的字典和Python字典)旨在
让你能够轻松地找到特定的单词(键),以获悉其定义(值)。
在很多情况下,使用字典都比使用列表更合适。
假设有如下名单:

>>> names = ['Alice', 'Beth', 'Cecil', 'Dee-Dee', 'Earl'] 

如果要创建一个小型数据库,在其中存储这些人的电话号码,该如何办呢?一种办法是再创
建一个列表。假设只存储四位的分机号,这个列表将类似于:

>>> numbers = ['2341', '9102', '3158', '0142', '5551'] 

创建这些列表后,就可像下面这样查找Cecil的电话号码:

>>> numbers[names.index('Cecil')] 
'3158' 

这可行,但不太实用。实际上,你希望能够像下面这样做:

>>> phonebook['Cecil'] 
'3158' 

如何达成这个目标呢?只要phonebook是个字典就行了。

4.2 创建和使用字典

字典以类似于下面的方式表示:

phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'} 

字典由键及其相应的值组成,这种键-值对称为项(item)
每个键与其值之间都用冒号(:)分隔,项之间用逗号分隔,而整个字典放在花括号内。
空字典(没有任何项)用两个花括号表示,类似于下面这样:{}。

在字典(以及其他映射类型)中,键必须是独一无二的,而字典中的值无需如此。

4.2.1 函数 dict

可使用函数dict①从其他映射(如其他字典)或键-值对序列创建字典。

① 与list、tuple和str一样,dict其实根本就不是函数,而是一个类。

>>> items = [('name', 'Gumby'), ('age', 42)] 
>>> d = dict(items) 
>>> d 
{'age': 42, 'name': 'Gumby'} 
>>> d['name'] 
'Gumby' 

还可使用关键字实参来调用这个函数,如下所示:

>>> d = dict(name='Gumby', age=42) 
>>> d 
{'age': 42, 'name': 'Gumby'} 

尽管这可能是函数dict最常见的用法,但也可使用一个映射实参来调用它,这将创建一个字
典,其中包含指定映射中的所有项。像函数list、tuple和str一样,如果调用这个函数时没有提
供任何实参,将返回一个空字典。从映射创建字典时,如果该映射也是字典(毕竟字典是Python
中唯一的内置映射类型),可不使用函数dict,而是使用字典方法copy,这将在本章后面介绍。

4.2.2 基本的字典操作

字典的基本行为在很多方面都类似于序列。
 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 ? 
IndexError: list assignment index out of range 
>>> x = {} 
>>> x[42] = 'Foobar' 
>>> x 
{42: 'Foobar'} 

首先,我尝试将字符串'Foobar'赋给一个空列表中索引为42的元素。这显然不可能,因为
没有这样的元素。要让这种操作可行,初始化x时,必须使用[None] * 43之类的代码,而不能使
用[]。然而,接下来的尝试完全可行。这次我将'Foobar'赋给一个空字典的键42;如你所见,这
样做一点问题都没有:在这个字典中添加了一个新项。

4.2.3 将字符串格式设置功能用于字典

第3章介绍过,可使用字符串格式设置功能来设置值的格式,这些值是作为命名或非命名参
数提供给方法format的。在有些情况下,通过在字典中存储一系列命名的值,可让格式设置更容
易些。例如,可在字典中包含各种信息,这样只需在格式字符串中提取所需的信息即可。为此,
必须使用format_map来指出你将通过一个映射来提供所需的信息。

>>> phonebook 
{'Beth': '9102', 'Alice': '2341', 'Cecil': '3258'} 
>>> "Cecil's phone number is {Cecil}.".format_map(phonebook) 
"Cecil's phone number is 3258." 

像这样使用字典时,可指定任意数量的转换说明符,条件是所有的字段名都是包含在字典中
的键。在模板系统中,这种字符串格式设置方式很有用(下面的示例使用的是HTML)。

>>> template = '''<html> 
... <head><title>{title}</title></head> 
... <body> 
... <h1>{title}</h1> 
... <p>{text}</p> 
... </body>''' 
>>> data = {'title': 'My Home Page', 'text': 'Welcome to my home page!'} 
>>> print(template.format_map(data)) 
<html> 
<head><title>My Home Page</title></head> 
<body> 
<h1>My Home Page</h1> 
<p>Welcome to my home page!</p> 
</body> 

4.2.4 字典方法

字典的方法很有用,但其使用频率可能没有列表和字符串的方法那样高。

  1. clear
    方法clear删除所有的字典项,这种操作是就地执行的(就像list.sort一样),因此什么都不
    返回(或者说返回None)。
>>> d = {} 
>>> d['name'] = 'Gumby' 
>>> d['age'] = 42 
>>> d 
{'age': 42, 'name': 'Gumby'} 
>>> returned_value = d.clear() 
>>> d 
{} 
>>> print(returned_value) 
None 

这为何很有用呢?我们来看两个场景。下面是第一个场景:

>>> x = {} 
>>> y = x 
>>> x['key'] = 'value' 
>>> y 
{'key': 'value'} 
>>> x = {} 
>>> x = {} 
{'key': 'value'} 

下面是第二个场景:

>>> x = {} 
>>> y = x 
>>> x['key'] = 'value' 
>>> y 
{'key': 'value'} 
>>> x.clear() 
>>> y 
{} 
  1. copy
    方法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']}

直接赋值、浅拷贝和深度拷贝解析

可使用模块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']} 
  1. fromkeys
    方法fromkeys创建一个新字典,其中包含指定的键,且每个键对应的值都是None。
>>> {}.fromkeys(['name', 'age']) 
{'age': None, 'name': None} 

这个示例首先创建了一个空字典,再对其调用方法fromkeys来创建另一个字典,这显得有点
多余。你可以不这样做,而是直接对dict(前面说过,dict是所有字典所属的类型。类和类型将
在第7章详细讨论)调用方法fromkeys。

>>> dict.fromkeys(['name', 'age']) 
{'age': None, 'name': None}

如果你不想使用默认值None,可提供特定的值。

>>> dict.fromkeys(['name', 'age'], '(unknown)') 
{'age': '(unknown)', 'name': '(unknown)'}
  1. get
    通常,如果你试图访问字典中没有的项,将引发错误。
>>> d = {} 
>>> print(d['name']) 
Traceback (most recent call last): 
 File "<stdin>", line 1, in ? 
KeyError: 'name' 

而使用get不会这样:

>>> print(d.get('name')) 
None

如你所见,使用get来访问不存在的键时,没有引发异常,而是返回None。你可指定“默认”
值,这样将返回你指定的值而不是None。

>>> d.get('name', 'N/A') 
'N/A' 

如果字典包含指定的键,get的作用将与普通字典查找相同。

>>> d['name'] = 'Eric' 
>>> d.get('name') 
'Eric' 

下面是这个程序的运行情况。注意到get提高了灵活性,让程序在用户输入的值出乎意料时
也能妥善处理。

Name: Gumby 
Phone number (p) or address (a)? batting average 
Gumby's batting average is not available. 
  1. items
    方法items返回一个包含所有字典项的列表,其中每个元素都为(key, value)的形式。字典项
    在列表中的排列顺序不确定。
>>> d = {'title': 'Python Web Site', 'url': 'http://www.python.org', 'spam': 0} 
>>> d.items() 
dict_items([('url', 'http://www.python.org'), ('spam', 0), ('title', 'Python Web Site')])

返回值属于一种名为字典视图的特殊类型。字典视图可用于迭代(迭代将在第5章详细介绍)。
另外,你还可确定其长度以及对其执行成员资格检查。

>>> it = d.items() 
>>> len(it) 
3 
>>> ('spam', 0) in it 
True 

视图的一个优点是不复制,它们始终是底层字典的反映,即便你修改了底层字典亦如此。

>>> d['spam'] = 1 
>>> ('spam', 0) in it 
False 
>>> d['spam'] = 0 
>>> ('spam', 0) in it 
True 

然而,如果你要将字典项复制到列表中(在较旧的Python版本中,方法items就是这样做的),
可自己动手做。

>>> list(d.items()) 
[('spam', 0), ('title', 'Python Web Site'), ('url', 'http://www.python.org')] 
  1. keys
    方法keys返回一个字典视图,其中包含指定字典中的键。

  2. pop
    方法pop可用于获取与指定键相关联的值,并将该键-值对从字典中删除。

>>> d = {'x': 1, 'y': 2} 
>>> d.pop('x') 
1 
>>> d 
{'y': 2}
  1. popitem
    方法popitem类似于list.pop,但list.pop弹出列表中的最后一个元素,而popitem随机地弹
    出一个字典项,因为字典项的顺序是不确定的,没有“最后一个元素”的概念。如果你要以高效
    地方式逐个删除并处理所有字典项,这可能很有用,因为这样无需先获取键列表。
>>> d = {'url': 'http://www.python.org', 'spam': 0, 'title': 'Python Web Site'} 
>>> d.popitem() 
('url', 'http://www.python.org') 
>>> d 
{'spam': 0, 'title': 'Python Web Site'} 

虽然popitem类似于列表方法pop,但字典没有与append(它在列表末尾添加一个元素)对应
的方法。这是因为字典是无序的,类似的方法毫无意义。

如果希望方法popitem以可预测的顺序弹出字典项,请参阅模块collections中的
OrderedDict类。

  1. setdefault
    方法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'}
>>> d = {} 
>>> print(d.setdefault('name')) 
None 
>>> d 
{'name': None} 

如果希望有用于整个字典的全局默认值,请参阅模块collections中的defaultdict类。

  1. update
    方法update使用一个字典中的项来更新另一个字典
>>> d = { 
... 'title': 'Python Web Site', 
... 'url': 'http://www.python.org', 
... 'changed': 'Mar 14 22:09:15 MET 2016' 
... } 
>>> x = {'title': 'Python Language Website'} 
>>> d.update(x) 
>>> d 
{'url': 'http://www.python.org', 'changed': 
'Mar 14 22:09:15 MET 2016', 'title': 'Python Language Website'} 

对于通过参数提供的字典,将其项添加到当前字典中。如果当前字典包含键相同的项,就替
换它。
可像调用本章前面讨论的函数dict(类型构造函数)那样调用方法update。这意味着调用
update时,可向它提供一个映射、一个由键-值对组成的序列(或其他可迭代对象)或关键字参数。

  1. values
    方法values返回一个由字典中的值组成的字典视图。不同于方法keys,方法values返回的视
    图可能包含重复的值。
>>> d = {} 
>>> d[1] = 1 
>>> d[2] = 2 
>>> d[3] = 3 
>>> d[4] = 1 
>>> d.values() 
dict_values([1, 2, 3, 1]) 
posted @ 2021-06-15 22:58  干饭啦  阅读(42)  评论(0编辑  收藏  举报