7. 字典

字典是 Python 中内建的一种具有弹性储存能力的数据结构,可存储任意类型对象,与序列使用整数索引不同,它使用键(key)进行索引。

通常任何不变类型的对象均可作为索引,比如数字,字符串和元组,列表可以被修改,不可作为键。由于键作为索引使用,所以它必须是唯一的。

字典的每个键都有对应的值 (value),键值对用冒号 “:” 分割,每个键值对之间用逗号 “,” 分割,整个字典包括在花括号 {} 中。

0
dict0 = {key0 : val0, key1 : value1}

7.1. 创建和访问字典

7.1.1. 直接创建字典

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
dict_empty = {} # 可以创建空字典

key = 'abc'
dict0 = {1: None,'abc': 1, (1, 2): "tuple key", key: "replaced"}
print (dict0)
print (dict0[1])
print (dict0[key])
print (dict0[(1,2)])

>>>
{1: None, 'abc': 'replaced', (1, 2): 'tuple key'}
None
replaced
tuple key

可以看到如果,出现重复的键,比如这里的 ‘abc’ ,则最后的一个键值会替换前面的,键对应的值可以是任意数据类型, 不同的键可以对应相同的值。

7.1.2. 访问字典

字典以键为索引访问对应的值,如果键不存在,抛出 KeyError :

0
1
2
3
4
5
6
print(dict0[2])

>>>
  File "C:/Users/Red/.spyder/dictest.py", line 16, in <module>
    print (dict0[2])

KeyError: 2

D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.

字典方法 D.get() 方法可以在键不存在时返回指定的值,如果不指定则默认返回 None 。

0
1
2
3
4
5
print(dict0[2])
print(dict0.get(2, "hello"))

>>>
None
hello

7.1.3. 间接创建字典

包含键值对的( key-value)序列,使用 dict() 进行类型转换。

0
1
2
3
4
5
list0 = [('key0', 1), ('key2', None), (3, ['1', '2'])] # 可以是元组类型
dict0 = dict(list0)
print(dict0)

>>>
{'key0': 1, 'key2': None, 3: ['1', '2']}

通过参数对序列,也可以创建字典,但是关键字必须是字符串:

0
1
2
3
4
dict0 = dict(key0=1, key1="abc")
print(dict0)

>>>
{'key0': 1, 'key1': 'abc'}

7.1.3.1. 字典推导

类似列表推导,字典推导(dict comprehension),可以简化代码。

0
1
2
3
4
5
6
7
dict0 = {x: x**2 for x in [1, 2, 3]}
dict1 = {x: "/home/" + x + '.jpg' for x in ("pic0", "pic1")}
print(dict0)
print(dict1)

>>>
{1: 1, 2: 4, 3: 9}
{'pic0': '/home/pic0.jpg', 'pic1': '/home/pic1.jpg'}

7.1.3.2. zip 合并

zip()函数名副其实,它的作用很像拉链,将两个列表合并成一个 zip 对象,dict() 可以把它转化为字典。

0
1
2
3
4
dict0 = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
print(dict0)

>>>
{'one': 1, 'two': 2, 'three': 3}

7.1.3.3. 由列表生成定值字典

D.fromkeys(iterable, value=None, /) method of builtins.type instance
   Returns a new dict with keys from iterable and values equal to value.

D.fromkeys() 方法支持从迭代对象取键,并可指定值的字典。通常使用列表或者元组作为参数。

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
seq = ['key0', 'key1']             # 也可为元组,字符串等可迭代对象
dict0 = dict.fromkeys(seq)         # 字典所有值均为 None
dict1 = dict.fromkeys(seq, 10)     # 字典所有值均为 10
dict2 = dict.fromkeys(seq, [1, 2]) # 字典所有值均为 [1, 2]
dict3 = dict.fromkeys('123', 10)   # 一次从字符串中取一个字符作为键

for i in range(4):
    print("dict%d:\t%s" % (i, eval("dict" + str(i))))

>>>
dict0:  {'key0': None, 'key1': None}
dict1:  {'key0': 10, 'key1': 10}
dict2:  {'key0': [1, 2], 'key1': [1, 2]}
dict3:  {'1': 10, '2': 10, '3': 10}

如果序列中出现重复成员,在生成的字典中它作为键只会出现一次。

7.2. 字典操作

7.2.1. 键值添加和更新

0
1
2
3
4
5
6
7
8
9
dict0 = {}
dict0['key0'] = "val0"  # 添加键值对
print(dict0)

dict0['key0'] = 123     # 更新键的值
print(dict0)

>>>
{'key0': 'val0'}
{'key0': 123}

7.2.2. 键值不存在时更新

D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D

与直接对键赋值不同,D.setdefault() 方法可以在键存在时不做操作,而在键不存在时更新键值对。

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
dict0 = {'key0': 'val0'}
dict0['key1'] = 'val1' # 直接赋值
print(dict0)

dict0.setdefault('key1', "newval") # key1 存在,不做操作
print(dict0)
dict0.setdefault('key2', "newval") # key2 不存在,插入
print(dict0)

>>>
{'key0': 'val0', 'key1': 'val1'}
{'key0': 'val0', 'key1': 'val1'}
{'key0': 'val0', 'key1': 'val1', 'key2': 'newval'}

7.2.3. 更新键值对

D.update() 方法把一个迭代对象(通常为字典)中的键值对更新到当前字典中,如果键存在则覆盖。

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
dict0 = {'key0': 'val0'}
dict1 = {'key0': 0, "key1" : [1, 2]}
dict0.update(dict1)
dict0.update([("name", "value")]) # 其他含键值对的可迭代对象

# 即便释放 dict1 不影响 dict0 值,是完全复制
del(dict1)
print(dict0)

>>>
{'key0': 0, 'key1': [1, 2], 'name': 'value'}

7.2.4. 删除键值和清空字典

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
dict0 = {"key0" : "val0", "key1" : "val1"}
del dict0['key0'] # 删除键值
print(dict0)

dict0.clear()     # 清空字典
print(dict0)

del(dict0)        # 删除dict0变量,释放资源
print(dict0)      # NameError 找不到 dict0 变量

>>>
{'key1': 'val1'}
{}

......
NameError: name 'dict0' is not defined

del() 函数删除dict0变量,不可再被使用。D.clear() 方法只清空字典,字典可以被访问。

7.2.5. 按键访问并删除

D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised

D.pop() 方法删除字典给定键 key 所对应的成员,并返回它对应的值,如果键不存在返回参数指定的默认值。

0
1
2
3
4
5
6
7
8
dict0 = {'key0': 0, 'key1': [1, 2]}
print(dict0.pop('key0', "default"))
print(dict0)
print(dict0.pop('key5', "default"))

>>>
0
{'key1': [1, 2]}
default

7.2.6. 随机遍历访问

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
dict0 = {"key0" : "val0", "key1" : "val1"}
for i in dict0:          # 默认迭代字典键序列
    print(i, end=' ')

print("\n")
for i in dict0.values(): # 迭代字典值序列
    print(i, end=' ')

print("\n")
for i in dict0.items():  # 迭代字典键值对
    print(i)

>>>
key0 key1

val0 val1

('key0', 'val0')
('key1', 'val1')

使用字典内建的 D.values() 方法和 D.items()方法可以方便循环处理每一个成员。

7.2.7. 遍历删除

D.popitem() 内建方法随机返回并删除字典中的一对键和值,为元组类型。字典不可为空,否则会报错。

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
dict0 = {'key0': 0, 'key1': [1, 2], 'name': 'value'}
for i in range(len(dict0)):
   item = dict0.popitem()
   print(item)

print(dict0)   # 空字典
>>>
('name', 'value')
('key1', [1, 2])
('key0', 0)
{}

示例中可以看出字典是无序的,并没有按照成员赋值的顺序,而是按照键的 ASCII 码排序。

7.2.8. 字典复制

类似列表,字典也支持深浅拷贝,字典自带的 D.copy() 方法是浅拷贝,借助 copy 模块实现深拷贝。

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
dict0 = {'key0': 'val0', 'list' : [1, 2, 3]}

dict1 = dict0          # 引用对象
dict2 = dict1.copy()   # 浅拷贝:只复制父对象一级,子对象不复制,还是引用

import copy
dict3 = copy.deepcopy(dict0) #深拷贝,完全复制

dict1['key0'] = 'newval'
del dict1['key0']      # 删除会影响引用 dict0
dict0['list'][0] = 'a' # 改变子对象值,影响浅拷贝 dict2,不影响深拷贝 dict3

for i in range(4):
    print("dict%d:\t%s" % (i, eval("dict" + str(i))))

>>>
dict0:  {'list': ['a', 2, 3]}
dict1:  {'list': ['a', 2, 3]}
dict2:  {'key0': 'val0', 'list': ['a', 2, 3]}
dict3:  {'key0': 'val0', 'list': [1, 2, 3]}

7.2.9. 字典和字符串转换

通过 str() 类型转化方法可以把字典转换位字符串:

0
1
2
3
4
5
dict0 = {'key0': 'val0', 'list' : [1, 2, 3]}
str0 = str(dict0)
print(str0)

>>>
{'key0': 'val0', 'list': [1, 2, 3]}

字符串转字典通常有两种方式,eval() 方法和 json 模块提供的 json.loads() 方法。

0
1
2
3
4
5
6
7
8
9
dict0 = eval(str0) # eval 方法
print(dict0)

import json        # 使用 json 模块
dict1 = json.loads("\"" + str0 + "\"") # 或 repr(str0)
print(dict1)

>>>
{'key0': 'val0', 'list': [1, 2, 3]}
{'key0': 'val0', 'list': [1, 2, 3]}

注意,采用 json 模块时字符串前后必须添加引号,简单的方式为 repr(str0)

7.2.10. 字典相等比较

Python2.x 版本使用 cmp() 方法比较字典,Python3 取消了该方法,直接使用比较运算符。

0
1
2
3
4
5
6
7
8
dict0 = {'key0': 0, 'key1': [1, 2]}
dict1 = {'key0': 0, 'key2': [1, 2]}

print(dict0 == dict1)
print(dict0 != dict1)

>>>
False
True

字典不可以比较大小,只可以比较是否相等,相等即指字典所有的键值对完全相同。

7.2.11. 字典排序

由于字典本身是无需的,所以需要转换为有序的对象,例如列表。字典对象的 items() 方法可以转换为可迭代对象,迭代对象的元素为元组,形式为 (‘key’, value),然后使用 sorted 函数通过参数 key 指定排序所用的关键字。

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 按 key 排序
In [58]: scores = {'John': 15, 'Bill': 18, 'Kent': 12}
    ...: new_scores = sorted(scores.items(), key=lambda x:x[1], reverse=False)
    ...: print(new_scores)
    ...:
[('Kent', 12), ('John', 15), ('Bill', 18)]

# 按 value 排序
In [59]: scores = {'John': 15, 'Bill': 18, 'Kent': 12}
    ...: new_scores = sorted(scores.items(), key=lambda x:x[0], reverse=False)
    ...: print(new_scores)
    ...:
[('Bill', 18), ('John', 15), ('Kent', 12)]

7.3. 统计和存在判定

7.3.1. 统计字典元素个数

0
1
2
3
4
dict0 = {'key0': 'val0', 'key1' : "val1"}
print(len(dict0))

>>>
2

7.3.2. 键存在判定

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
dict0 = {'key0': 'val0', 'key1' : "val1"}

#print(dict0.has_key('a'))    # False
#print(dict0.has_key('key0')) # True

# Python3.x 不再支持 has_key() 方法,被 __contains__(key) 替代
print(dict0.__contains__('a'))
print(dict0.__contains__('key0'))

# 或者使用 key in 判断,not in 执行反操作
print('a' in dict0)
print('a' not in dict0)

>>>
False
True
False
True

通常使用 in 或者 not in 成员运算符。