Python-dictionary
[Reference]
[1] https://docs.python.org/3/library/stdtypes.html#typesmapping
1. dict
格式和特性
A mapping object maps hashable values to arbitrary objects. Mappings are mutable objects. There is currently only one standard mapping type, the dictionary.
映射对象将可散列的值映射为任意对象。映射是可变对象,目前只有一种标准映射类型:字典。
注意
-
虽然字典的键(key)几乎是任意可散列值(不可散列的值:
list
、dict
或其他可变对象); -
用于键的数字类型遵循数字比较规则,因此可以使用浮点数(近似)但是不建议;
-
可散列:如果一个对象是可散列的,那么这个对象的生命周期中,其对应的散列值是不可变的,而且这个对象需要实现
__hash__()
方法,同时可散列对象还需要__eq__()
方法,方便和其他键值进行比较。通常原子不可变数据类型str
、bytes
和数值类型都是可散列的; -
一般来讲用户自定义的类型都是可散列的,通过
id()
函数即可获取其对应的散列值,在没有创建__eq__()
方法,所有这些对象都是不等的,假设一个对象实现该方法且用到了该对象的内部状态,那么当所有这些内部状态均不可变,这个对象才是可散列的; -
frozenset
也是可散列的(因为frozenset
只能容纳可散列类型)
字典创建
字典可以通过在大括号中逗号分隔的多个键值对key: value
创建,典型如下:
dict_a = {'jack': 4098, 'sjoerd': 4127}
dict_a = {x: x ** 2 for x in range(10)}
字典推导dict([('a', 100), ('b', 200)])
元组解包
补充事项
- 如果没有给出位置参数(
key: value
),则创建一个空字典; - 如果给出了一个位置参数并且它是一个映射对象,则使用与映射对象相同的键值对创建一个字典,否则,位置参数必须是可迭代对象;
- 可迭代对象中的每个项目本身必须是一个恰好有两个对象的可迭代对象。每个项目的第一个对象成为新字典中的键,第二个对象成为相应的值;
- 如果某个键出现多次,则该键的最后一个值将成为新字典中的相应值。
- 如果给出了关键字参数,则关键字参数及其值将添加到从位置参数创建的字典中。如果要添加的键已经存在,则来自关键字参数的值将替换来自位置参数的值。
2. dict
初始化
通常我们只会用第一节所写的方式初始化字典 {key
: value
, …},参考如下代码,官方文档中还给出了其他的初始化方式:
- 使用zip将对应的键和值
list
捆绑成键值对; - 使用多个元组构成的
list
,根据元组的解包特性实现;这里注意:传入dict的可以是[]/()
对象,其成员满足包含两个元素; - 混合使用花括号和
=
; - 使用字典推导。
""" initial dict """
a = dict(one=1, two=2, three=3)
b = {'one': 1, 'two': 2, 'three': 3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
d = dict([('two', 2), ('three', 3), ('one', 1)])
e = dict({'three': 3, 'one': 1, 'two': 2})
f = dict({'one': 1, 'three': 3}, two=2)
info = [(1, 'one'),
(2, 'two'),
(3, 'three')]
g = {key: value for value, key in info}
print(a == b == c == d == e == f == g)
除了以上这些方式,还可以通过字典的方法:fromkeys()
,从序列中批量创建指定值的字典,详情见后。
res = dict.fromkeys(range(10), 1)
3. dict
方法
这里直接拉取官方文档的方法。详情参考后续展开部分。
list(d)
len(d)
d[key]
d[key] = value
del d[key]
key in d / key not in d
iter(d)
clear()
copy()
get(key, [, default])
items()
keys()
values()
pop(key, [, default])
popitem()
reversed(d)
setdefault()
update()
d | other
d |= other
后续使用的例子a
均使用字典推导式(其原理和列表推导式一致)。
3.1 list(d)
将字典的键返回成list
,参考如下列子:
a = {x: x * 2 for x in range(6)}
print('d = {}'.format(a))
print('Test list(d): {}'.format(list(a)))
# 测试结果
d = {0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10}
Test list(d): [0, 1, 2, 3, 4, 5]
3.2 len(d)
返回字典的键值对(key/value)个数。
a = {x: x * 2 for x in range(6)}
len(a) # res: 6
3.3 d[key]
根据键从字典获取对应值的信息。
a = {x: x * 2 for x in range(6)}
a[0] # a[0] = 0
3.4 d[key] = value
设置键的新值。
a = {x: x * 2 for x in range(6)}
a[0] = 100
a
# 测试结果
{0: 100, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10}
3.5 del d[key]
删除指定的键值对
a = {x: x * 2 for x in range(6)}
del a[0]
a
# 测试结果
{1: 2, 2: 4, 3: 6, 4: 8, 5: 10}
3.6 key in d
判断字典中是否存在对应的键**key
**,非 用法key not in d
a = {x: x * 2 for x in range(6)}
print('Key verify: 0 in a: [{}], 7 in a: [{}], 8 not in a: [{}]'.format(0 in a, 7 in a, 8 not in a))
Key verify: 0 in a: [True], 7 in a: [False], 8 not in a: [True]
3.7 iter(d)
基于字典的键返回一个迭代器,其对应iter(d.keys())
a = {x: x * 2 for x in range(6)}
print(list(iter(a)))
# 测试结果
[0, 1, 2, 3, 4, 5]
3.8 clear()
清空字典,删除所有项目。
a = {x: x * 2 for x in range(6)}
print('a = {}'.format(a))
a.clear()
print('Use clear(): a = {}'.format(a))
a = {0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10}
Use clear(): a = {}
3.9 copy()
拷贝字典,需要注意这里的拷贝是浅拷贝。这里的浅拷贝不同于list
的浅拷贝,字典的浅拷贝实际是深拷贝父对象(一级目录),子对象(二级目录)不拷贝而是引用。直接使用=
赋值,则是使用引用。
import copy
a = {x: x * 2 for x in range(3)}
a[4] = [1, 2, 3]
b = a # reference assignment.
c = a.copy() # just deep copy 1st layer
d = copy.deepcopy(a) # all deep copy
# alter b[0], b[4]
b[0] = 7
b[4].append(4)
print('a = {}, b = {}, c = {}, d = {}'.format(a, b, c, d))
print('a == b is {}'.format(a == b))
# alter c[0], c[4]
c[0] = 9
c[4].remove(1)
print('a = {}, b = {}, c = {}, d = {}'.format(a, b, c, d))
# 测试结果
a = {0: 7, 1: 2, 2: 4, 4: [1, 2, 3, 4]}, b = {0: 7, 1: 2, 2: 4, 4: [1, 2, 3, 4]}, c = {0: 0, 1: 2, 2: 4, 4: [1, 2, 3, 4]}, d = {0: 0, 1: 2, 2: 4, 4: [1, 2, 3]}
a == b is True
a = {0: 7, 1: 2, 2: 4, 4: [2, 3, 4]}, b = {0: 7, 1: 2, 2: 4, 4: [2, 3, 4]}, c = {0: 9, 1: 2, 2: 4, 4: [2, 3, 4]}, d = {0: 0, 1: 2, 2: 4, 4: [1, 2, 3]}
stackoverflow 关于该讨论给出了几个数据共享示意图。对应上述代码可知:
- b 即 a 的引用
- c 共享了一级的键值,对于值中的二级可变对象或者嵌套字典则为引用;
- d 完全地独立
3.10 classmethod fromkeys(iterable[, value])
基于可迭代对象(典型如 list
)和初值,创建一个键为可迭代对象的值,值为初值的字典,即所有的值都引用自一个实例。
注意:如果未设置初值 value
,则默认为 None
seq_list = ['google', 'facebook', 'microsoft', 'alibaba', 'tencent']
seq_tuple = ('google', 'facebook', 'microsoft', 'alibaba', 'tencent')
a = dict.fromkeys(seq_list)
b = dict.fromkeys(seq_tuple)
print('a == b is {}'.format(a == b))
print(a)
c = dict.fromkeys(seq_tuple, 1) # set value is 1
d = dict.fromkeys(seq_tuple, [1, 2, 3, 4]) # set value is list: [1,2,3,4]
print(c)
print(d)
# test result
a == b is True
{'google': None, 'facebook': None, 'microsoft': None, 'alibaba': None, 'tencent': None}
{'google': 1, 'facebook': 1, 'microsoft': 1, 'alibaba': 1, 'tencent': 1}
{'google': [1, 2, 3, 4], 'facebook': [1, 2, 3, 4], 'microsoft': [1, 2, 3, 4], 'alibaba': [1, 2, 3, 4], 'tencent': [1, 2, 3, 4]}
3.11 get(key[, default])
同3.3 获取对应key
的value
,如果字典中不存在,则返回默认值(None
),如果默认值不存在则抛出KeyError
。
a = {x: x * 2 for x in range(3)}
a.get(1) # 2
3.12 items()
获取字典全部键值对的新视图(元组数组)。
a = {x: x * 2 for x in range(3)}
print(a.items())
# test result
dict_items([(0, 0), (1, 2), (2, 4)])
3.13 keys()
返回字典中全部键(key
)
a = {x: x * 2 for x in range(3)}
print(a.keys())
dict_keys([0, 1, 2])
3.14 pop(key[, default])
如果键在字典中,则移除键并且返回value
,否则返回默认值,如果默认值不存在,则抛出keyError
。
a = {x: x * 2 for x in range(3)}
print(a.pop(2))
print(a)
# test result
4
{0: 0, 1: 2}
3.15 popitem()
从键值中按栈(LIFO: 后进先出)顺序删除并返回对应的键值对。LIFO需要ver.>=3.7
a = {x: x * 2 for x in range(3)}
print('{}->{}->{}'.format(a.popitem(), a.popitem(), a.popitem()))
# test result
(2, 4)->(1, 2)->(0, 0)
3.16 reversed()
在字典的键上返回一个反向迭代器,其相当于reversed(d.keys())
a = {x: x * 2 for x in range(3)}
b = list(reversed(a))
print(b)
# test result
[2, 1, 0]
3.17 setdefault(key[, default])
如果键存在则返回对应的值,反之添加该键并将值设为默认值。
a = {x: x * 2 for x in range(3)}
a.setdefault(4)
print(a)
# test result
{0: 0, 1: 2, 2: 4, 4: None}
3.18 update([other])
使用其他的键值对覆盖现有键,返回None
。other可以是另一个字典对象或者键/值对的可迭代对象(元组)。如果指定关键字参数,字典,然后用这些键/值对更新:d.update(red=1, blue=2)
。
update()
函数处理参数m
的方式,是典型的 duck typing
,ref1、ref2。函数首先检查m是否有keys方法,如果有,那么update()
函数就把它当做映射对象处理。否则函数会退一步,转而把m
当做包含了键值对(key, value)
元素的迭代器。
a = {x: x * 2 for x in range(3)}
b = {x: x ** 2 for x in range(4)}
a.update(b)
print(a)
b.update(age=25)
print(b)
a.update(name='naruto', age=20)
print(a)
b.update({'age': 25})
print(b)
# test result
{0: 0, 1: 1, 2: 4, 3: 9}
{0: 0, 1: 1, 2: 4, 3: 9, 'age': 25}
{0: 0, 1: 1, 2: 4, 3: 9, 'name': 'naruto', 'age': 20}
{0: 0, 1: 1, 2: 4, 3: 9, 'age': 25}
3.19 values()
返回字典中所有键对应的值。
a = {x: x * 2 for x in range(3)}
print(list(a.values()))
# test result
[0, 2, 4]
3.20 d | other
合并 d 和 other 中的键和值来创建一个新的字典,两者必须都是字典。当 d 和 other 有相同键时, other 的值优先。注意仅支持 python>= 3.9
a = {x: x * 2 for x in range(3)}
other = {x: x ** 2 for x in range(3)}
b = a | other
print(b)
3.21 d |= other
用 other 的键和值更新字典 d ,other 可以是 mapping 或 iterable 的键值对。当 d 和 other 有相同键时, other 的值优先。注意仅支持 python>= 3.9
a = {x: x * 2 for x in range(3)}
other = {x: x ** 2 for x in range(3)}
print(a |= other)