数据结构和算法-6-字典中的键映射多个值

问题描述

怎样实现一个键对应多个值的字典(也叫 multidict)?

解决方案

我们都知道一个字典就是一个键对应一个单值的映射。

如:{"one":1}

可如果我们想要让一个键映射多个值,那么就需要将这多个值放到另外的容器(列表或集合)中。

如:{"one":[1,2,3,4]}

选择使用列表还是集合取决于你的实际需求。

  • 如果你想保持元素的插入顺序就应该使用列表
  • 如果想去掉重复元素就使用集合(并且不关心元素的顺序)。

使用 defaultdict

我们可以很方便的使用 collections 模块中的 defaultdict 来构造这样的字典。

defaultdict 的一个特征是它会自动初始化每个 key 刚开始对应的值,所以我们只需要关注添加元素操作就行了。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from collections import defaultdict


def test_multidict_use_defaultdict():
"""使用defaultdict创建多值字典"""
# 用列表来存储多个值,会保持元素的插入顺序,且不会去掉重复的元素
d = defaultdict(list)

# 向「键」中加入多个值
d['name'].append("张三")
d['name'].append("李四")
d['name'].append("王二")
d['name'].append("王二")
d['age'].append(20)

assert d.get('name') == ['张三', '李四', '王二', '王二']
assert d.get('age') == [20]

# 用集合来存储多个值,会去掉重复的元素,且不关心元素顺序
d2 = defaultdict(set)

d2['name'].add("张三")
d2['name'].add("李四")
d2['name'].add("王二")
d2['name'].add("王二")
d2['name'].add("王二")
d2['name'].add("王二")
d2['age'].add(20)

assert d2.get('name') == {'李四', '张三', '王二'}
assert d2.get('age') == {20}

需要注意的是, defaultdict 会自动为将要访问的键(就算目前字典中并不存在这样的键)创建映射实体。

如果我们不需要这样的特性,可以在一个普通的字典上使用 setdefault() 方法来代替。

使用 setdefault

使用setdefault创建多值字典,比如:

1
2
3
4
5
6
7
8
9
10
def test_multidict_use_setdefault():
"""使用setdefault创建多值字典"""

d = {} # 一个普通的字典
d.setdefault('a', []).append(1) # 每次都要创建一个新的初始值实例
d.setdefault('a', []).append(2)
d.setdefault('b', []).append(4)

assert d.get('a') == [1, 2]
assert d.get('b') == [4]

但是很多程序员觉得 setdefault() 用起来有点别扭。因为每次调用都得创建一个新的初始值的实例(例子程序中的空列表 [] )。

自已动手

一般来讲,创建一个多值映射字典是很简单的。

但是,如果你选择自己实现的话,那么对于值的初始化可能会有点麻烦。

你可能会像下面这样来实现:

1
2
3
4
5
d = {}
for key, value in pairs:
if key not in d:
d[key] = []
d[key].append(value)

如果使用 defaultdict 的话代码就更加简洁了:

1
2
3
d = defaultdict(list)
for key, value in pairs:
d[key].append(value)
毕小烦 wechat
「请扫一扫上面的二维码,关注老毕的微信公众号」
「您的赞赏是老毕持续创作的动力」