一、基本介绍
在Python中,如果没有列表和字典是不可想象的。字典及其强有力的实现是使Python如此有效和出众的重要原因。本文将展示字典及其使用方法。
字典和列表之间可以相互转换,但字典和列表的不同之处在哪里?首先列表是有序的,而字典是无序的,但核心的不同在于字典是通过键(key)来访问,而列表是通过位置访问。更进一步讲,字典是一种抽象数据类型的实现。字典的每一个键都与一个值相关联。值可以是任意的Python数据类型。此外,字典不支持像位置索引、切片等序列操作方法。
二、字典实例
第一个例子是关于美国和加拿大的几个大城市人口的,首先创建一个名为city的字典,字典中包含一些城市以及这些城市包含的人口数量:
>>> city = {"New York City":8175133, "Los Angeles": 3792621, "Washington":632323, "Chicago": 2695598, "Toronto":2615060, "Montreal":11854442, "Ottawa":883391, "Boston":62600}
如果我们想得到某个城市的人口数量,我们可以直接用城市的名称进行查询:
>>> city["New York City"]
8175133
>>> city["Toronto"]
2615060
>>> city["Boston"]
62600
如果我们用city中不存在的城市名称作为key访问,则会报错。
>>> city["Detroit"]
Traceback (most recent call last):
File "<stdin>", line 1, in
KeyError: 'Detroit'
如果你回看下上面定义的字典city,你可能会觉得字典中的元素是有序的,例如,第一个是”New York City”, 第二个是 “Los Angeles” 等等,但事实上字典是无序的。我们再输出查看下city,得到的顺序可能与原始顺序是不一样的。
>>> city
{'Toronto': 2615060, 'Ottawa': 883391, 'Los Angeles': 3792621, 'Chicago': 2695598, 'New York City': 8175133, 'Boston': 62600, 'Washington': 632323, 'Montreal': 11854442}
因此,字典不能像列表一样通过一个数字索引来访问元素。
>>> city[0]
Traceback (most recent call last):
File "<stdin>", line 1, in
KeyError: 0
>>>
向字典添加元素很简单,直接赋值即可。
>>> city["Halifax"] = 390096
>>> city
{'Toronto': 2615060, 'Ottawa': 883391, 'Los Angeles': 3792621, 'Chicago': 2695598, 'New York City': 8175133, 'Halifax': 390096, 'Boston': 62600, 'Washington': 632323, 'Montreal': 11854442}
因此,通过往一个空字典中递增式添加元素从而获得所需字典的方式是可行的。前面还没有提到如何创建一个空字典。如下是创建一个名字为city的空字典的方式:
>>> city = {}
>>> city
{}
下一个例子是一个简单的英德字典:
en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}
print(en_de)
print(en_de["red"])
再定义一个德法字典:
de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}
现在,它可以从英语翻译成法语了。尽管我们没有一个英法字典,但是通过de_fr[en_de[“red”]] 我们能够得到”red”的法文翻译:”rouge”。
en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}
print(en_de)
print(en_de["red"])
de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}
print("The French word for red is: " + de_fr[en_de["red"]])
上面代码的输出是:
{'blue': 'blau', 'green': 'grün', 'yellow': 'gelb', 'red': 'rot'}
rot
The French word for red is: rouge
在字典中,我们可以用任意类型作为值(value),但是对键(key)是有约束的,只有不可变的类型才能作为键。如果用一个可变类型作为键,将会报错。下面将一个列表作为键(key),因为列表是可变类型,所以报错。
>>> dic = { [1,2,3]:"abc"}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list objects are unhashable
元组是不可变类型,因此可以作为键(key):
>>> dic = { (1,2,3):"abc", 3.1415:"abc"}
>>> dic
{3.1415: 'abc', (1, 2, 3): 'abc'}
我们还可以创建一个包含字典的字典:
en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}
de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}
dictionaries = {"en_de" : en_de, "de_fr" : de_fr }
print(dictionaries["de_fr"]["blau"])
输出为:
bleu
三、字典操作
例子:
下面是一个拉丁字符与其对应摩尔斯码的字典。
morse = {
"A" : ".-",
"B" : "-...",
"C" : "-.-.",
"D" : "-..",
"E" : ".",
"F" : "..-.",
"G" : "--.",
"H" : "....",
"I" : "..",
"J" : ".---",
"K" : "-.-",
"L" : ".-..",
"M" : "--",
"N" : "-.",
"O" : "---",
"P" : ".--.",
"Q" : "--.-",
"R" : ".-.",
"S" : "...",
"T" : "-",
"U" : "..-",
"V" : "...-",
"W" : ".--",
"X" : "-..-",
"Y" : "-.--",
"Z" : "--..",
"0" : "-----",
"1" : ".----",
"2" : "..---",
"3" : "...--",
"4" : "....-",
"5" : ".....",
"6" : "-....",
"7" : "--...",
"8" : "---..",
"9" : "----.",
"." : ".-.-.-",
"," : "--..--"
}
获取字典中包含的拉丁字符数量可以调用len函数:
>>> len(morse)
38
这个字典仅仅包含大写字母,因此检查小写字母”a”是否在字典中将返回False:
>>> "a" in morse
False
>>> "A" in morse
True
>>> "a" not in morse
True
list可以像栈(stack)一样使用pop()函数弹出一个元素。但是在字典中,因为是无序的并且没有索引,所以pop()函数有着不一样的含义。
如果D表示一个字典,那么D.pop(k)表示从D中移除键为k的元素,该操作的返回值是D[k]。
如果键k在D中不存在,那么将报错。
>>> en_de = {"Austria":"Vienna", "Switzerland":"Bern", "Germany":"Berlin", "Netherlands":"Amsterdam"}
>>> capitals = {"Austria":"Vienna", "Germany":"Berlin", "Netherlands":"Amsterdam"}>>> capital = capitals.pop("Austria")
>>> print(capital)
Vienna
>>> print(capitals)
{'Netherlands': 'Amsterdam', 'Germany': 'Berlin'}
>>> capital = capitals.pop("Switzerland")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Switzerland'
如果我们试图在上面的字典capitals中找到瑞士(Switzerland)的首都,由于不存在Switzerland,将报错。为了避免这种错误,我们可以在pop()函数中使用第二个参数作为缺省值。
>>> capital = capitals.pop("Switzerland", "Bern")
>>> print(capital)
Bern
>>> capital = capitals.pop("France", "Paris")
>>> print(capital)
Paris
>>> capital = capitals.pop("Germany", "München")
>>> print(capital)
Berlin
popitem()是字典的另一种方法,不需要任何输入参数,将随机移除字典中任意一个元素,返回值为该移除元素的键值对元组 (key,value)。如果是一个空字典,使用popitem()将报错。
>>> capitals = {"Springfield":"Illinois", "Augusta":"Maine", "Boston": "Massachusetts", "Lansing":"Michigan", "Albany":"New York", "Olympia":"Washington", "Toronto":"Ontario"}
>>> (city, state) = capitals.popitem()
>>> (city, state)
('Olympia', 'Washington')
>>> print(capitals.popitem())
('Albany', 'New York')
>>> print(capitals.popitem())
('Boston', 'Massachusetts')
>>> print(capitals.popitem())
('Lansing', 'Michigan')
>>> print(capitals.popitem())
('Toronto', 'Ontario')
>>>
如果我们访问字典中不存在的键,将得到错误信息:
>>> locations = {"Toronto" : "Ontario", "Vancouver":"British Columbia"}
>>> locations["Ottawa"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Ottawa'
可以通过”in”操作符进行事先检查,从而避免这个问题。
>>> if "Ottawa" in locations: print(locations["Ottawa"])
...
>>> if "Toronto" in locations: print(locations["Toronto"])
...
Ontario
另一种访问方式是通过get()方法,如果键不存在,get()方法不返回错误,而返回的是None。当然也可以设定一个键(key)不存在时缺省的返回值。
>>> proj_language = {"proj1":"Python", "proj2":"Perl", "proj3":"Java"}
>>> proj_language["proj1"]
'Python'
>>> proj_language["proj4"]
Traceback (most recent call last):
File "<stdin>", line 1, in
KeyError: 'proj4'
>>> proj_language.get("proj2")
'Perl'
>>> proj_language.get("proj4")
>>> print(proj_language.get("proj4"))
None
>>> # setting a default value:
>>> proj_language.get("proj4", "Python")
'Python'
一个字典可以使用copy()函数进行复制:
>>> w = words.copy()
>>> words["cat"]="chat"
>>> print(w)
{'house': 'Haus', 'cat': 'Katze'}
>>> print(words)
{'house': 'Haus', 'cat': 'chat'}
这种拷贝是浅拷贝。如果字典中的值是一个复杂的数据类型,例如list。
# -*- coding: utf-8 -*-
trainings = { "course1":{"title":"Python Training Course for Beginners",
"location":"Frankfurt",
"trainer":"Steve G. Snake"},
"course2":{"title":"Intermediate Python Training",
"location":"Berlin",
"trainer":"Ella M. Charming"},
"course3":{"title":"Python Text Processing Course",
"location":"München",
"trainer":"Monica A. Snowdon"}
}
trainings2 = trainings.copy()
trainings["course2"]["title"] = "Perl Training Course for Beginners"
print(trainings2)
检查下输出,我们能看到改变trainings中course2的title值,trainings2中也发生了一样的改变。
{'course2': {'trainer': 'Ella M. Charming', 'title': 'Perl Training Course for Beginners', 'location': 'Berlin'}, 'course3': {'trainer': 'Monica A. Snowdon', 'title': 'Python Text Processing Course', 'location': 'München'}, 'course1': {'trainer': 'Steve G. Snake', 'title': 'Python Training Course for Beginners', 'location': 'Frankfurt'}}
如果分配了一个新值给一个键,看看发生了什么:
trainings = { "course1":{"title":"Python Training Course for Beginners",
"location":"Frankfurt",
"trainer":"Steve G. Snake"},
"course2":{"title":"Intermediate Python Training",
"location":"Berlin",
"trainer":"Ella M. Charming"},
"course3":{"title":"Python Text Processing Course",
"location":"München",
"trainer":"Monica A. Snowdon"}
}
trainings2 = trainings.copy()
trainings["course2"] = {"title":"Perl Seminar for Beginners",
"location":"Ulm",
"trainer":"James D. Morgan"}
print(trainings2["course2"])
输出结果:
{'trainer': 'Ella M. Charming', 'location': 'Berlin', 'title': 'Intermediate Python Training'}
检查下输出,我们能看到改变trainings中course2的值,trainings2中course2却没有改变。如果你想了解这种现象产生的原因,可以回顾下前期文章《Python的浅拷贝和深拷贝》。
字典可以使用clear()函数清空,执行该方法后,字典本身没有被删除,而是被置为空字典。
>>> w.clear()
>>> print(w)
{}
能不能将字典合并或归并在一起呢,答案是肯定的。update()函数能够归并2个字典,如果存在相同的键,那么前一个字典该键下的值将被覆盖:
>>> knowledge = {"Frank": {"Perl"}, "Monica":{"C","C++"}}
>>> knowledge2 = {"Guido":{"Python"}, "Frank":{"Perl", "Python"}}
>>> knowledge.update(knowledge2)
>>> knowledge
{'Frank': {'Python', 'Perl'}, 'Guido': {'Python'}, 'Monica': {'C', 'C++'}}
对于字典的键(key)迭代,可以直接使用for循环和”in”操作符:
>>> d = {"a":123, "b":34, "c":304, "d":99}
>>> for key in d:
... print(key)
...
b
c
a
d
当然也可以使用keys()方法,能够得到相同的效果:
>>> for key in d.keys():
... print(key)
...
b
c
a
d
values()方法能够方便的对值(value)进行遍历。
>>> for value in d.values():
... print(value)
...
34
304
123
99
上面的循环等价于如下的循环:
for key in d:
print(d[key])
但这种没有第一种效率高。如果你熟悉ipyton的timeit使用方法,你可以测试下这两种方法的效率差别:
In [5]: %%timeit d = {"a":123, "b":34, "c":304, "d":99}
for key in d.keys():
x=d[key]
...:
1000000 loops, best of 3: 225 ns per loop
In [6]: %%timeit d = {"a":123, "b":34, "c":304, "d":99}
for value in d.values():
x=value
...:
10000000 loops, best of 3: 164 ns per loop
四、列表和字典的相互转换
list转换成dict,或者dict转换成list是使用python时经常会遇到的情况。我们可以通过dict的item()、keys()和values()方法来创建list。
>>> w = {"house":"Haus", "cat":"", "red":"rot"}
>>> items_view = w.items()
>>> items = list(items_view)
>>> items
[('house', 'Haus'), ('cat', ''), ('red', 'rot')]
>>>
>>> keys_view = w.keys()
>>> keys = list(keys_view)
>>> keys
['house', 'cat', 'red']
>>>
>>> values_view = w.values()
>>> values = list(values_view)
>>> values
['Haus', '', 'rot']
>>> values_view
dict_values(['Haus', '', 'rot'])
>>> items_view
dict_items([('house', 'Haus'), ('cat', ''), ('red', 'rot')])
>>> keys_view
dict_keys(['house', 'cat', 'red'])
下面再看看列表如何转换成字典。我们有两个列表,一个包含各种美食,一个是与这些美食相对应的国家。
>>> dishes = ["pizza", "sauerkraut", "paella", "hamburger"]
>>> countries = ["Italy", "Germany", "Spain", "USA"]
现在我们创建一个字典,将国家作为key,将该国家对应的美食作为value。首先构建一个新列表,这里我们使用了zip函数。
>>> country_specialities = list(zip(countries, dishes))
>>> print(country_specialities)
[('Italy', 'pizza'), ('Germany', 'sauerkraut'), ('Spain', 'paella'), ('USA', 'hamburger')]
然后通过dict()方法将列表转换成字典。
>>> country_specialities_dict = dict(country_specialities)
>>> print(country_specialities_dict)
{'Germany': 'sauerkraut', 'Italy': 'pizza', 'USA': 'hamburger', 'Spain': 'paella'}
这里关于zip函数仍然有一个问题,如果两个列表的长度不一致,会发生什么?其实很容易回答:多余的,没有可配对的元素将被忽略。
>>> dishes = ["pizza", "sauerkraut", "paella", "hamburger"]
>>> countries = ["Italy", "Germany", "Spain", "USA"," Switzerland"]
>>> country_specialities = list(zip(countries, dishes))
>>> country_specialities_dict = dict(country_specialities)
>>> print(country_specialities_dict)
{'Germany': 'sauerkraut', 'Italy': 'pizza', 'USA': 'hamburger', 'Spain': 'paella'}