kaindy7633/blog

Python之旅:第四章-当索引行不通时:使用字典

kaindy7633 opened this issue · 0 comments

Table of Contents generated with DocToc

Python之旅:第四章 当索引行不通时:使用字典

前面几个章节介绍的序列是一种通过索引(编号)来访问各个元素的数据结构。通过名称来访问其各个元素值的数据结构,叫做映射(mapping),而字典则是Python中唯一的内置映射类型。字典中的值是没有顺序的,而是存储在键下,键可以是数字、字符串或元组。

字典的用途

字典的名称指出了这种数据结构的用途。字典旨在让你能轻松的找到特定的单词(键),以获得其定义的(值)

例如:如果我们要创建一个人的序列,但需要根据人来找到其电话号码,则必须创建两个list,然后一一对应的找到其值,使用列表是可行的,但使用字典是最佳方式,且效率更高。

创建和使用字典

创建字典类似下面的这种表示方式:

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

字典由键及其相应的值组成,这种键-值对称为(item),每个键与其值之间都用冒号(:)分隔,项之间用逗号分隔,而整个字典放在花括号内。空字典直接使用两个花括号表示:{}。特别注意:在字典中的键必须是唯一的,而值则无需如此。

函数dict

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

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

还可以使用关键字实参来调用这个函数:

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

就像函数listtuplestr一样,如果调用dict函数时没有提供任何实参,将返回一个空字典。

基本的字典操作

字典的基本行为在很多方面都类似于序列

  • len(d): 返回字典d包含的项(键值对)的数量
  • d[k]: 返回与键k相关联的值。
  • d[k] = v: 将值v关联到键k
  • del d[k]: 删除键为k的项
  • k in d: 检查字典d是否包含键为k的项

字典与序列的不同之处:

  • 键的类型:序列中只有索引,类似于字典中的键,但它只能为整数,而字典中的键可以是整数,也可以是任何不可变的类型,比如浮点数、字符串或元组
  • 自动添加:即便是字典中原本没有的键,也可以给它赋值,这将在字典中创建一个新项,然后,我们不能给列表中没有的元素赋值
  • 成员资格:在字典中,表达式k in d(d是一个字典),查找的是键,而不是值,在序列中,表达式v in l(l是一个列表),查找的是值而不是索引。

字典中的键可以是任何不可变的类型,这一点是字典的主要优点,看下面的示例:

>>> x = []
>>> x[42] = 'Foobar'
Traceback (most recent call last):
  File "<pyshell#130>", line 1, in <module>
    x[42] = 'Foobar'
IndexError: list assignment index out of range
>>> x = {}
>>> x[42] = 'Foobar'
>>> x
{42: 'Foobar'}

如果要给空的列表添加新的项,上面的操作是不可行的,然而,字典却可以。

看下面的例子:

# 一个简单的数据库

# 一个将人名用作键的字典,每个人都用一个字典表示
# 字典包含键'phone'和'addr',它们分别与电话号码和地址相关联

people = {
  'Alice': {
    'phone': '2341',
    'addr': 'Foo drive 23'
  },

  'Beth': {
    'phone': '9102',
    'addr': 'Bar street 42'
  },

  'Cecil': {
    'phone': '3158',
    'addr': 'Baz avenue 90'
  }
}

# 电话号码和地址的描述性标签,供打印输出时使用
labels = {
  'phone': 'phone number',
  'addr': 'address'
}

name = input('Name: ')

# 要查找电话号码还是地址?
request = input('Phone number (p) or address (a)? ')

# 使用正确的键
if request == 'p': key = 'phone'
if request == 'a': key = 'addr'

# 仅当名字是字典包含的键时才打印信息:
if name in people:
  print("{}'s {} is {}.".format(name, labels[key], people[name][key]))

运行结果如下:

Name: Alice
Phone number (p) or address (a)? p
Alice's phone number is 2341.

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

通过字典,我们可以使用format_map来指定一个映射来提供所需的信息:

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

像这样使用字典,可指定任意数量的转换说明符,条件是所有的字段名都是包含在字典中的键,这在模板系统中非常有用。

>>> 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>

字典方法

clear

方法clear删除所有的字典项,这种操作就地执行,因此什么都不会返回,或者说返回None

>>> d = {}
>>> d['name'] = 'Gumby'
>>> d['age'] = 42
>>> d
{'name': 'Gumby', 'age': 42}
>>> returned_value = d.clear()
>>> d
{}
>>> print(returned_value)
None

下面有两种场景需要注意,看代码:

# 第一种场景
>>> x = {}
>>> y = x # x和y都指向同一个字典对象
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x = {} # 当给x赋值空字典后,y依然指向原来的对象
>>> y
{'key': 'value'}
>>> x = {}
>>> y = x # x和y都指向同一个字典对象
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x.clear() # 当使用clear清空x后,y的指向也被删除了
>>> y
{}

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']}

fromkeys

方法fromkeys创建一个新字典,其中包含指定的键,且每个键对应的值都是None

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

上面的行为有点多余,你可以直接使用dict来调用fromkeys方法:

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

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

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

get

方法get为访问字典项提供了宽松的环境,通常情况下,如果我们访问字典中没有的项,会引发错误:

>>> d = {}
>>> print(d['name'])
Traceback (most recent call last):
  File "<pyshell#192>", line 1, in <module>
    print(d['name'])
KeyError: 'name'

而使用get则不会:

>>> d = {}
>>> print(d.get('name'))
None

当使用get来访问字典中不存在的键时,没有引发异常,而是返回了None,我们还可以指定默认值:

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

如果字典中包含指定的值,则它与普通的字典查找相同。

下面是一个使用get查找简单数据库的示例:

# 一个使用get()的简单数据库

people = {
  'Alice': {
    'phone': '2341',
    'addr': 'Foo drive 23'
  },

  'Beth': {
    'phone': '9102',
    'addr': 'Bar street 42'
  },

  'Cecil': {
    'phone': '3158',
    'addr': 'Baz avenue 90'
  }
}

labels = {
  'phone': 'phone number',
  'addr': 'address'
}

name = input('Name: ')

# 要查找电话号码还是地址?
request = input('Phone number(p) or address (a)? ')

# 使用正确的键
key = request # 如果request既不是'p'也不是'a'
if request == 'p': key = 'phone'
if request == 'a': key = 'addr'

# 使用get提供默认值
person = people.get(name, {})
label = labels.get(key, key)
result = person.get(key, 'not available')

print("{}'s {} is {}.".format(name, label, result))

运行结果如下:

Name: Beth
Phone number(p) or address (a)? p
Beth's phone number is 9102.

itmes

方法items返回一个包含所有字典项的列表,其中每个元素都为(key, value)的形式,字典项咋系列表中的排列顺序不确定。

>>> d = {'title': 'Python Web Site', 'url': 'https://www.python.org', 'spam': 0}
>>> d.items()
dict_items([('title', 'Python Web Site'), ('url', 'https://www.python.org'), ('spam', 0)])

返回值是一种名为字典视图的特殊类型,字典视图可用于迭代,我们还可以获取其长度以及执行成员资格检查

>>> 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

我们也可以手动复制字典项到列表中

>>> list(d.items())
[('title', 'Python Web Site'), ('url', 'https://www.python.org'), ('spam', 0)]

keys

方法keys返回一个字典视图,其中包含指定字典中的键

pop

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

>>> d = {'x': 1, 'y': 2}
>>> d.pop('x')
1
>>> d
{'y': 2}

popitem

方法popitem随机的弹出一个字典项,因为字典项的顺序是不确定的,没有'最后一个元素'的概念。

>>> d = {'title': 'Python Web Site', 'url': 'https://www.python.org', 'spam': 0}
>>> d.popitem()
('spam', 0)
>>> d
{'title': 'Python Web Site', 'url': 'https://www.python.org'}

setdefault

方法setdefault有点像get,因为它也获取与指定键相关联的值,但除此之外,它还在字典不包含指定的键时,在字典中添加指定的键值对

>>> d = {}
>>> d.setdefault('name', 'N/A')
'N/A'
>>> d
{'name': 'N/A'}
>>> d['name'] = 'Gumby'
>>> d.setdefault('name', 'N/A')
'Gumby'
>>> d
{'name': 'Gumby'}

update

方法update使用一个字典的项来更新另一个字典

>>> d = {
	'title': 'Python Web Site',
	'url': 'https://www.python.org',
	'changed': 'Mar 14 22:09:15 MET 2016'
	}
>>> x = {'title': 'Python Language Website'}
>>> d.update(x)
>>> d
{'title': 'Python Language Website', 'url': 'https://www.python.org', 'changed': 'Mar 14 22:09:15 MET 2016'}

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])

本章节完毕

本系列目录: