12 字典dict

前述章节讲述了字符串、列表和元组等集合类型,这三种集合都是有序的集合类型,有的可以修改有的不能修改,本章讲解另外两种集合dict和set即字典和set,它们是非有序的集合,即集合里的各项数据间不是有序地排列着,不能通过索引即位置信息来访问集合里的每个元素的值,那如何遍历这两类集合,是一个问题随着本章的进行会得到解决的办法的。

12.1 字典语义与用途

首先说Python里的字典也是一种集合类型,既然是集合那就是一堆数据用逗号间隔,用花括号括起来。定义看上去和之前的列表、字符串、元组很相似,关键是字典这类集合里数据很特殊!字典里的每项数据不是一个单纯的数据而是称之为一对儿的数据组合,即字典的每项数据有两项且用分号间隔,分号前称之为键key,分号后称之为值value,例如"a" : 12、"liming" : 13类似这样的构成字典的一项数据。

d = {1 : 2, "a" : 13, 12.4 : 77}
print d

程序执行结果:

{'a': 13, 1: 2, 12.4: 77}

看上去好乱!别急,首先要看到,字典d定义时的顺序和打印输出顺序不完全一致,这说明字典真的是无序的集合。另外作为键的可以是任意类型的数据均可,不像索引必须是整形,这是字典这种数据类型的一个重要特点。字典的键要求必须是能够哈希的、唯一不重复,因此字典的键的哈希值应该在对象的生命周期中是不会变的,这样数据查询效果更高、更快,这也就是字典这种集合类型存在的现实意义。如果还未明白字典的意图,可以看下面的例子:

a = [70, 89, 98, 65]
b = {"liming" : 98, "libo" : 89, "wanli" : 70, "xili" : 65}

对于列表a存储了四个数据,对于字典b实际也存储了四个数据,对于列表a怎样知道"libo"的成绩是多少了?但有了字典这种数据可以在b里很可查到"libo"的成绩。尤其是在数据量很大的情况下从列表里找出数据要比从字典里找到某数据要慢的多。通过字典可以构造出类似于数据库里的数据表,这样应用时查询也就更符合实际应用了。

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
print d
print d["wangli"]

程序执行结果:

{'liming': 98, 'wangli': 95, 'liping': 88, 'mali': 90}
95

字典d等价于下面的一张表格

姓名 成绩
liming 98
wangli 95
liping 88
mali 90

12.2 字典的访问

12.2.1 通过键值

字典的数据是由key:value对儿构成的每项数据,那么想访问某项数据的value需要可以通过[]运算来获得,其语法结构如下:

字典名[key]

12.2.2 字典的get函数

字典有个等价函数get可以获得这种方式的相同结果,语法结构如下:

字典名.get(key)

get函数返回值就是这个key所对应的值。

例如:

d = {1 : 2, "a" : 13, 12.4 : 77}
print d.get("a")

但是如果无key的话,get返回None而不会报错发生异常,而用[]运算即字典名[key]则会报错异常。

12.2.3 字典的setdefault函数

还有一个函数setdefault() 函数,它和get函数类似,返回指定键的值,如果键不在字典中,将会添加键并将值设置为一个指定值,默认为None。

get() 和 setdefault() 区别: setdefault() 返回的键如果不在字典中,会添加键(更新字典),而 get() 不会添加键。

12.3 字典遍历

字典不是有序的集合,就不能通过index来遍历了,那如何遍历字典呢?

方法一:直接用字典

for key in a_dict:
    print a_dict[key]

通过这样的结构可以的。

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
for key in d:
    print key, d[key]

程序执行结果如下:

liming 98
wangli 95
liping 88
mali 90

方法二:用字典的keys()返回值

for key in a_dict.keys():
    print a_dict[key]

方法一和方法二完全等价。

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
for key in d.keys():
    print key, d[key]

方法三:字典值values()

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
for v in d.values():
    print v

缺点,没有key信息。

方法四:items函数既有key又有value

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
print d.items()
for (k,v) in d.items():
    print k, v

items函数返回字典每项的key和value是一个有两项数据的tuplue元组构成的列表。

[('liming', 98), ('wangli', 95), ('liping', 88), ('mali', 90)]
liming 98
wangli 95
liping 88
mali 90

12.4 字典的修改

字典创建好了以后,可能数据需要修改可以通过以下几种方式修改字典。

12.4.1 通过键或update函数修改对应值

如果想修改字典某项数据可以通过键来修改对应值。

字典[key] = newvalue

例如:

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
d["wangli"] = 58

如果字典里不存在此key,那么就给字典新增一项数据。

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
print d
d["yangmi"] = 55
print d

程序执行结果:

{'liming': 98, 'wangli': 95, 'liping': 88, 'mali': 90}
{'liming': 98, 'yangmi': 55, 'wangli': 95, 'liping': 88, 'mali': 90}

update函数也可实现更新或者增加字典数据项的功能。

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
print d
e = {"yangmi" : 30, "reliya" : 33}
d.update(e)
print d
e = {"yangmi" : 13}
d.update(e)
print d

程序执行结果:

{'liming': 98, 'wangli': 95, 'liping': 88, 'mali': 90}
{'liming': 98, 'wangli': 95, 'reliya': 33, 'mali': 90, 'yangmi': 30, 'liping': 88}
{'liming': 98, 'wangli': 95, 'reliya': 33, 'mali': 90, 'yangmi': 13, 'liping': 88}

第一次update时字典d里是没有"yangmi"等key,那么字典e被加到了字典d里了,第二次的字典e里的"yangmi"这个key在字典里已存在,故只是更新了这个key所对应的值。

12.4.2 删除某项

删除字典某项数据可以用del函数或者pop函数。del的使用语法格式如下:

del 字典[key]

例如:

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
del d["wangli"]

或者用字典的pop函数删除指定key数据项,示例如下:

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
print d.pop("wangli")
print d

但是pop函数指定的key值不存在,则会报错异常,所以为了解决此问题,pop的用法通常是两个参数,一个是准备要删除的数据的key,另一个是假设key不存在的情况下返回某值,这样就不会报错异常了。

字典名.pop(key, value)

注意这里的value是在key不存在时pop的返回值,如果key存在pop返回key所对应的value值。

d = {"liming" : 98, "wangli":95, "mali":90, "liping" : 88}
ret = d.pop("liming")
print ret, d
ret = d.pop("liming", -1)
print ret, d
ret = d.pop("liming")
print ret, d

程序执行结果如下:

98 {'wangli': 95, 'liping': 88, 'mali': 90}
-1 {'wangli': 95, 'liping': 88, 'mali': 90}
Traceback (most recent call last):
  File "test.py", line 88, in <module>
    ret = d.pop("liming")
KeyError: 'liming'

第一次pop,键"liming"在字典d里是存在的,返回其对应值98,pop以后字典d里就没有“liming”这个key了,所以第二次pop时返回值为-1没有发生出错异常,而第三次pop的时候由于没有指定value而报错异常,所以pop(key, value)这种方式比较安全。

还有一个函数popitem是随机删除字典里的一项数据,返回的是这项数据的key和value组成的一个元组类型数据。

12.5 构造字典

字典是多个键值对构成的集合,创建一个字典除了按定义赋值键值对外,还有很多其他的方式可以创建字典。

12.5.1 键值对创建字典

这种方式适合体量较小的字典。

d = {1 : 12, "a" : 34, "name" : 13}

12.5.2 dict函数创建字典

可以借助字典的dict函数来创建一个字典,形式很多。

(1)变量赋值的方式

参数是带变量赋值的方式、赋值符号前是键值key(此种方式键不能为数,否则会被视为表达式),赋值符号后是值value,看上去很奇怪,以后解释,想创建几项就有写入几个这样的参数。

d = dict(b = 12, a = 34, name = 13)
#d = dict(1 = 12, a = 34, name = 13)
print d

(2)二元组集合方式

另外二元组集合(有两个元素的集合)也可作为dict函数参数,从而构造出一个以二元组第一值为键,第二个数据项值为值的字典。

i = [(1, 3), ("a", 13)]
d = dict(i)
print d
i = ((1, 3), ("a", 13))
d = dict(i)
print d
i = [[1, 2], ("a", 23)]
d = dict(i)
print d
i = ([1, 1], ["a", 33])
d = dict(i)
print d

程序执行结果如下:

{'a': 13, 1: 3}
{'a': 13, 1: 3}
{'a': 23, 1: 2}
{'a': 33, 1: 1}

(3) zip方式

zip函数可以将多个集合对应位置元素构成一个元组建立一个长度为多个集合中最短值的集合。

a = range(5)
b = range(10, 17)
c = range(20, 30)
print a
print b
print c
d = zip(a, b, c)
print d

程序执行结果如下:

[0, 1, 2, 3, 4]
[10, 11, 12, 13, 14, 15, 16]
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[(0, 10, 20), (1, 11, 21), (2, 12, 22), (3, 13, 23), (4, 14, 24)]

a集合的长度最短为5,b为7而c的长度为10,d集合的构建是从a、b、c对应位置取值组建一个元组作为d对应位置的数据项,例如:a[1] = 1, b[1] = 11, c[1] = 21,那么(1, 11, 21)作为d[1]的值。

节组zip的特性,可以给zip函数传两个(有序)集合,第一个集合里的各项元素值作为键,第二个集合里的各项数据值作为新生成字典的值。

例子: ASCII码表

ascii_c = []
ascii_v = range(0x41, 0x5b)
print ascii_v
for v in range(0x41, 0x5b):
    ascii_c.append(chr(v))
print ascii_c
d = dict(zip(ascii_c, ascii_v))
print d
print d["T"]

程序执行结果:

[65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
['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']
{'A': 65, 'C': 67, 'B': 66, 'E': 69, 'D': 68, 'G': 71, 'F': 70, 'I': 73, 'H': 72, 'K': 75, 'J': 74, 'M': 77, 'L': 76, 'O': 79, 'N': 78, 'Q': 81, 'P': 80, 'S': 83, 'R': 82, 'U': 85, 'T': 84, 'W': 87, 'V': 86, 'Y': 89, 'X': 88, 'Z': 90}
84

(4) map方式

zip函数仅仅对传入的集合对应位置取值,构建对儿或者元组,对应位置的数据间是没有任何关系的,如果我们想存储一条直线的x和y值的字典(目的是给出x值就知道这条直线上y的值)这是最终的字典的key和value间是有一定关系的,那么可以用map的结果作为dict的参数构造字典。

例子:假设存储直线$y = 2x + 1$的坐标数据用字典存储

a = range(-5, 5)
def linef(x):
    return (x, 2 * x + 1)
d = dict(map(linef, a))
print d

程序执行结果:

{0: 1, 1: 3, 2: 5, 3: 7, 4: 9, -2: -3, -5: -9, -4: -7, -3: -5, -1: -1}

map函数的第一个参数是一个函数,从第2个参数开始是有序集合,map的作用原理是对后续所有的有序集合的第i个位置的数据都传入到第一个参数处理,故map里有多少个有序集合,map函数的第一个参数的那个函数就应该有多少个形参。

本例子用map实现方式定义的函数(map的第一个参数)有很多形式和方法,本例还以用下面的代码实现。

a = range(-5, 5)
b = []
for v in a:
    b.append(2 * v + 1)
def linef(x, y):
    return (x, y)
d = dict(map(linef, a, b))
print d

可能这样的代码更容易理解map函数的功能。

12.6 字典综合示例

查找200万条记录里是否含有9527?消费刷卡流水单据表,第一项是流水号码从0变化到200万减1,第二项是银行卡号后四位,变化从9500~9599共一百张卡。在这200万消费流水记录里查找9527消费的流水号。

参考程序

import random
a = range(2000000)
b = range(9500, 9600)
print len(b)
def kv(k):
    x = random.randint(0, 99)
    v = b[x]
    return (k, v)
d = dict(map(kv, a))
print len(d)
for k, v in d.items():
    if v == 9527:
        print k

必要的解释import等价于C语言的include,既引入库,C语言里想打印要用printf函数,程序设计者可以自己写输出函数也可用标准C提供的已写好的输出函数printf函数,那么就的用include 将stdio.h包含到程序设计者写的代码里,本例子参考程序想随机发生9527消费记录,就随机产生了一个x,b[x]代表的就是随机消费卡号。

如有任何一问可发邮件交流: mailiao艾特126点儿com <== 这个你懂的。

感谢Klang(金浪)智能数据看板klang.org.cn鼎力支持!