dictionary - 在一个表达式,如何合并两个Python词典?

  显示原文与译文双语对照的内容

我有两个 python 字典,我想写一个返回这两个字典的表达式,合并。 update() 方法是我需要的,如果它返回结果而不是修改一个 dict in-place 。


>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = x.update(y)
>>> print z
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

如何在z 中得到最终合并的dict,而不是x?

于以及。相关 ), ( 要 extra-clear,last-one-wins conflict-handling dict.update() 正是我期待

时间:

在你的情况下,你可以做的是:


z = dict(x.items() + y.items())

这将把最终的dict放在 z 中,并使 b的值被dict的第二个( y ) 值正确覆盖:


>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

如果你使用 python 3,它只是稍微复杂一点。 要创建 z:


>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

另一个更简洁,更简洁的选项:


z = dict(x, **y)

注意: 这已经成为一种流行的答案,但重要的是要指出,如果 y 有任何non-string钥匙,根本的事实,这种方式的效果是一个滥用一个CPython的实现细节了,可是有什么用在 CPython 3.2,或者在 PyPy,IronPython,或者 jhyton 。 另外,不是一个粉丝 。 所以我不能推荐这个技术用于forward-compatible或者cross-implementation可移植代码,这意味着它应该完全避免。

在后续的回答中,你询问了这两个备选方案的相对性能:


z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

在我的机器上,至少( 一个相当普通的x86_64运行 python 2.5.2 ),可选的z2 不仅短而且简单,而且更快。 使用带有 timeit 模块,你可以自行验证这一点。

示例 1: 相同的字典映射 20个连续整数:


% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z2 以 3.5或者更高的比例胜出。 不同的字典似乎产生了不同的结果,但 z2 似乎总是在前面。 以数字大小超过默认 3 。), ( 如果在同一测试,请尝试传入 -r 则将获得不一致的结果。

示例 2: non-overlapping字典将 252个短字符串映射到整数,反之亦然:


% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)' 
10000 loops, best of 3: 26.9 usec per loop

z2 获胜大约为 10. 这是我的书中相当大的胜利 !

在比较了这两者之后,我想知道 z1的性能是否能归因于构建两个项目列表的开销,这反过来导致了这种变化是否能更好地工作:


from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

一些快速测试,e.g.


% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

让我得出结论,z3z1 快一些,但不如 z2 快。 绝对不值得额外的打字。

这里讨论仍然缺少一些重要的内容,这是与"明显"合并两个列表的方式的性能比较: 使用 update 方法。为了使事情与表达式保持相等的基础,我将复制一个x,而不是修改它 in-place,如下所示:


z0 = dict(x)
z0.update(y)

典型的结果:


% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

换句话说,z0z2 似乎有相同的性能。 你认为这可能是一个巧合? 我没有。。

事实上,我想说的是,纯 python 代码做任何比这更好的事情。 和一个C 扩展模块中如果你能做到重大进步,我想象了 python 民间完全可以有兴趣将你的代码是( 或者你的方法的变化) 到 python 核心。 python 在许多地方使用 dict ;优化它的操作是一个很大的问题。

你也可以把这写成


z0 = x.copy()
z0.update(y)

就像Tony所做的那样,但是( 不足为奇)的符号差异对性能没有任何可以衡量的影响。 使用任何适合你的外观。 当然,他绝对可以指出two-statement版本更容易理解。

我想要类似的东西,但有能力指定如何合并重复键的值,所以我把它 hacked ( 但没有对它进行严重的测试) 。 显然这不是单个表达式,但它是一个单独的函数调用。


def merge(d1, d2, merge_fn=lambda x,y:y):
"""
 Merges two dictionaries, non-destructively, combining 
 values on duplicate keys as defined by the optional merge
 function. The default behavior replaces the values in d1
 with corresponding values in d2. (There is no other generally
 applicable merge strategy, but often you'll have homogeneous 
 types in your dicts, so specifying a merge technique can be 
 valuable.)

 Examples:

>> > d1
 {'a': 1, 'c': 3, 'b': 2}
>> > merge(d1, d1)
 {'a': 1, 'c': 3, 'b': 2}
>> > merge(d1, d1, lambda x,y: x+y)
 {'a': 2, 'c': 6, 'b': 4}

"""
 result = dict(d1)
 for k,v in d2.iteritems():
 if k in result:
 result[k] = merge_fn(result[k], v)
 else:
 result[k] = v
 return result

如果不使用副本,我可以想到的最好版本是:


from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

它比 dict(x.items() + y.items()) 快,但不如快 n = copy(a); n.update(b) ,至少在CPython上。 这个版本也可用作 python 3如果更改 iteritems()items(),它是自动由 2完成到3 工具。

我个人喜欢这个版本最好的,因为它描述了我在单一功能语法中所需要的东西。 唯一的小问题是,它不做太直观,将值从y 优先于x 中的值,但我不相信它是困难发现这一点

在 python 3中,你可以使用 collections.ChainMap,它将多个dicts或者其他映射组合在一起,创建一个可以更新的视图:


>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = ChainMap({}, y, x)
>>> for k, v in z.items():
 print(k, '-->', v)

a --> 1
b --> 10
c --> 11


x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

对于两个字典中带有键的项(''),你可以通过将最后一个放在输出中来控制它。

...