Python中字典对象的合并

229
如何在Python中计算两个dict对象的并集,其中只有在任一字典中存在(key, value)对时才出现在结果中(除非有重复项)?
例如,{'a': 0, 'b': 1}{'c': 2}的并集是{'a': 0, 'b': 1, 'c': 2}
最好不要修改任何输入dict。此方法实用的例子: 获取所有当前范围内变量及其值的字典

3
尽管标题不是这样,另一个问题在询问**d2语法是什么。它碰巧也提供了对这个问题的答案。 - Mechanical snail
8
在Python 3.5及以上版本中,可以使用{**dict1, **dict2}将两个字典合并为一个字典。这种方法比其他解决方案更可靠,因为它无论键是什么类型都可以正常工作,而不会出现错误。 - Nathan
merged_dict = dict(**a,**b,**c,**d) 对我也起作用了。 - Charlie Parker
2
看起来正确的解决方案将在Python 3.9中推出,请参见https://www.python.org/dev/peps/pep-0584/。 - Attila123
3
Python 3.9: a = {1: 'a', 2: 'b', 3: 'c'}; b = {4: 'd', 5: 'e'}; c = a | b Python 3.9:a = {1: 'a', 2: 'b', 3: 'c'}; b = {4: 'd', 5: 'e'}; c = a | b - Pygirl
返回翻译后的文本:执行{**d1, **d2}。详情请参考:https://dev59.com/43VD5IYBdhLWcg3wQJOT - Charlie Parker
4个回答

170

这个问题提供了一个俗语。您可以将其中一个字典用作dict()构造函数的关键字参数:

dict(y, **x)

当出现重复时,会优先保留x中的值;例如:

dict({'a' : 'y[a]'}, **{'a', 'x[a]'}) == {'a' : 'x[a]'}

16
"简单胜于复杂。" :) 您应该使用 dictupdate 成员函数。 - shahjapan
29
tmp = dict(y); tmp.update(x); do_something(tmp) 更简洁吗? - Mechanical snail
9
这并不复杂,这是很好地利用了Python字典结构。而且这与更新(update)不同(这个解决方案没有更新任何内容)。 - lajarre
20
这段话的意思是:这不是“好的”,它是神秘的,它会让大多数读者退缩,剩下的人会认为x中所有的键都必须是合法的参数名。在我看来,它能够工作是因为实现中的名称检查机制存在漏洞。当你依赖漏洞时会发生什么?它们要么被修复,要么成为PEP过程中的政治棒子。 - Jon Jay Obermark
16
同意。dict(x=2) 是可以的,但 dict(4=2) 不可以。因此,如果 x={4:2},那么所提出的方法将失败。 - Miguel
显示剩余10条评论

126

你也可以使用字典的update方法,例如:

a = {'a' : 0, 'b' : 1}
b = {'c' : 2}

a.update(b)
print a

3
请注意,.update会更改a。有时,这是不可接受的。 - Boris Gorelik
4
使用 {**d1, **d2} 可以合并两个字典。详情请参考:https://dev59.com/43VD5IYBdhLWcg3wQJOT - Charlie Parker
为什么会失败?self.indices_to_labels = dict(**self.labels_to_indices, **global_label2global_indices) - Charlie Parker

68

对于静态字典,可以合并其他字典的快照:

从Python 3.9开始,定义了二进制“或”运算符|用于连接字典(会立即创建一个新的具体字典):

>>> a = {"a":1}
>>> b = {"b":2}
>>> a|b
{'a': 1, 'b': 2}

相反地,|= 增强赋值已经被实现为与调用 update 方法相同:

>>> a = {"a":1}
>>> a |= {"b": 2}
>>> a
{'a': 1, 'b': 2}

更多细节请查看PEP-584

在Python 3.9之前,创建新字典的简单方法是使用“星号展开”方式,直接添加每个子字典的内容:

c = {**a, **b}

对于动态字典组合,作为“视图”来组合、实时的字典:

如果你需要两个字典保持独立,而且可更新,你可以创建一个单一对象,在它的__getitem__方法中查询这两个字典(并实现get__contains__和其他映射方法,根据需要)。

一个极简示例可以是这样的:

class UDict(object):
   def __init__(self, d1, d2):
       self.d1, self.d2 = d1, d2
   def __getitem__(self, item):
       if item in self.d1:
           return self.d1[item]
       return self.d2[item]

而且它可以正常工作:

>>> a = UDict({1:1}, {2:2})
>>> a[2]
2
>>> a[1]
1
>>> a[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __getitem__
KeyError: 3
>>> 

NB:如果想懒惰地维护两个或多个字典的联合视图,请查看标准库中的collections.ChainMap - 它具有所有字典方法,并涵盖了上面示例未考虑到的角落情况。


1
执行 {**d1, **d2}。详情请参见:https://dev59.com/43VD5IYBdhLWcg3wQJOT - Charlie Parker
抱歉@neuron - 您的编辑破坏了答案。第二部分不仅适用于旧版本,它描述了如何创建自定义映射,可以引用独立的子词典,这与语言版本无关。 (我应该在几分钟内回来并将其修复,但请注意在编辑时文本/代码的含义) - jsbueno
为什么会失败?self.indices_to_labels = dict(**self.labels_to_indices, **global_label2global_indices) - Charlie Parker

38

两个字典

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

n 个字典

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

18
更通俗易懂的写法是:dict(i for dct in dicts for i in dct.items())。这个表达式的意思不变,它可以将多个字典合并成一个字典。 - Eric
为什么要转换为list()? def union2(dict1, dict2): return dict(dict1.items() + dict2.items()) - kinORnirvana
2
@kinORnirvana 在 Python 3 中:a = {'x': 1}; type(a.items()) => <class 'dict_items'> - Mathieu Larose
@kinORnirvana "为什么要转换成list()?" 因为否则会出现:"TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'"。 - Attila123
1
执行 {**d1, **d2}。详情请参见:https://dev59.com/43VD5IYBdhLWcg3wQJOT - Charlie Parker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接