如何在单个表达式中合并两个Python词典?
对于字典x和y,z成为浅合并字典,y中的值替换x中的值。
在Python 3.5或gre …
对于Python 2:
x = {'a':1, 'b': 2} y = {'b':10, 'c': 11} z = dict(x.items()+y.items()) print(z)
对于Python 3:
x = {'a':1, 'b': 2} y = {'b':10, 'c': 11} z = dict(x.items()|y.items()) print(z)
它给出了输出: {'a': 1, 'c': 11, 'b': 10}
{'a': 1, 'c': 11, 'b': 10}
这可以通过单个字典理解来完成:
>>> x = {'a':1, 'b': 2} >>> y = {'b':10, 'c': 11} >>> { key: y[key] if key in y else x[key] for key in set(x) + set(y) }
在我看来,“单一表达”部分的最佳答案是不需要额外的功能,而且很短。
虽然问题已经多次回答, 这个问题的简单解决方案尚未列出。
x = {'a':1, 'b': 2} y = {'b':10, 'c': 11} z4 = {} z4.update(x) z4.update(y)
它与z0和上面提到的邪恶z2一样快,但易于理解和改变。
借鉴这里和其他地方的想法,我理解了一个功能:
def merge(*dicts, **kv): return { k:v for d in list(dicts) + [kv] for k,v in d.items() }
用法(在python 3中测试):
assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\ {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'}) assert (merge(foo='bar')=={'foo': 'bar'}) assert (merge({1:11},{1:99},foo='bar',baz='quux')==\ {1: 99, 'foo': 'bar', 'baz':'quux'}) assert (merge({1:11},{1:99})=={1: 99})
你可以使用lambda代替。
>>> x = {'a':1, 'b': 2} >>> y = {'b':10, 'c': 11} >>> x, z = dict(x), x.update(y) or x >>> x {'a': 1, 'b': 2} >>> y {'c': 11, 'b': 10} >>> z {'a': 1, 'c': 11, 'b': 10}
我想要类似的东西,但能够指定复制键上的值是如何合并的,所以我将其破解(但没有对它进行大量测试)。显然这不是单个表达式,而是单个函数调用。
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
这可能不是一个流行的答案,但你几乎肯定不想这样做。如果你想要一个合并的副本,那么使用副本(或 deepcopy的 ,取决于你想要的,然后更新。这两行代码比使用.items()+ .items()的单行创建更具可读性 - 更多Pythonic。显式优于隐式。
此外,当您使用.items()(Python 3.0之前)时,您正在创建一个包含dict项目的新列表。如果你的词典很大,那么这就是很多开销(一旦创建合并的dict就会抛弃两个大的列表)。 update()可以更有效地工作,因为它可以逐项运行第二个dict。
就......而言 时间 :
>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000) 15.52571702003479 >>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000) 15.694622993469238 >>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000) 41.484580039978027
IMO前两者之间的微小减速对于可读性来说是值得的。此外,字典创建的关键字参数仅在Python 2.3中添加,而copy()和update()将在旧版本中使用。
def deepupdate(original, update): """ Recursively update a dict. Subdict's won't be overwritten but also updated. """ for key, value in original.iteritems(): if key not in update: update[key] = value elif isinstance(value, dict): deepupdate(value, update[key]) return update
示范:
pluto_original = { 'name': 'Pluto', 'details': { 'tail': True, 'color': 'orange' } } pluto_update = { 'name': 'Pluutoo', 'details': { 'color': 'blue' } } print deepupdate(pluto_original, pluto_update)
输出:
{ 'name': 'Pluutoo', 'details': { 'color': 'blue', 'tail': True } }
谢谢rednaw的编辑。
滥用导致一个表达式的解决方案 马修的回答 :
>>> x = {'a':1, 'b': 2} >>> y = {'b':10, 'c': 11} >>> z = (lambda f=x.copy(): (f.update(y), f)[1])() >>> z {'a': 1, 'c': 11, 'b': 10}
你说你想要一个表达,所以我被滥用了 lambda 绑定名称和元组以覆盖lambda的one-expression限制。随意畏缩。
lambda
如果你不关心复制它,你当然也可以这样做:
>>> x = {'a':1, 'b': 2} >>> y = {'b':10, 'c': 11} >>> z = (x.update(y), x)[1] >>> z {'a': 1, 'b': 10, 'c': 11}
在您的情况下,您可以做的是:
z = dict(x.items() + y.items())
这将根据你的需要,将最终的词典放入 z ,并为key创建值 b 被第二个正确覆盖( y )dict的价值:
z
b
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}
即使答案对此有好处 浅 字典,这里定义的方法都没有实际进行深层字典合并。
示例如下:
a = { 'one': { 'depth_2': True }, 'two': True } b = { 'one': { 'extra': False } } print dict(a.items() + b.items())
人们会期待这样的结果:
{ 'one': { 'extra': False', 'depth_2': True }, 'two': True }
相反,我们得到这个:
{'two': True, 'one': {'extra': False}}
“one”条目应该具有“depth_2”和“extra”作为其字典中的项目,如果它真的是合并的话。
使用链也不起作用:
from itertools import chain print dict(chain(a.iteritems(), b.iteritems()))
结果是:
rcwesick给出的深度合并也会产生相同的结果。
是的,它可以合并样本字典,但它们都不是合并的通用机制。一旦我编写了一个执行真正合并的方法,我将在稍后更新。
替代:
z = x.copy() z.update(y)
在后续回答中,您询问了这两种备选方案的相对表现:
z1 = dict(x.items() + y.items()) z2 = dict(x, **y)
在我的机器上,至少(一个相当普通的x86_64运行Python 2.5.2),替代 z2 不仅更短更简单,而且速度更快。您可以使用自己验证这一点 timeit Python附带的模块。
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 似乎总是走在前面。 (如果你得到不一致的结果 相同 测试,尝试传入 -r 数字大于默认值3.)
-r
示例2:非重叠字典将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 可怜的表现可能归因于构建两个项目列表的开销,这反过来让我想知道这种变化是否可能更好:
z1
from itertools import chain z3 = dict(chain(x.iteritems(), y.iteritems()))
一些快速测试,例如
% 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
让我得出结论 z3 有点快 z1 ,但没有那么快 z2 。绝对不值得所有额外打字。
z3
这个讨论仍然缺少一些重要的东西,这是对这些备选方案的性能比较与合并两个列表的“明显”方式:使用 update 方法。为了尝试使表达式保持平等,没有一个修改x或y,我将复制x而不是就地修改它,如下所示:
update
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
换一种说法, z0 和 z2 似乎具有基本相同的性能。你认为这可能是巧合吗?我不....
z0
事实上,我甚至声称纯Python代码不可能做得更好。如果你可以在C扩展模块中做得更好,我想Python人员可能会对将你的代码(或你的方法的变体)合并到Python核心感兴趣。 Python使用 dict 在很多地方;优化其运营是一件大事。
dict
你也可以这样写
z0 = x.copy() z0.update(y)
正如Tony所做的那样,但(并不奇怪)表示法中的差异结果表明不会对性能产生任何可衡量的影响。使用适合您的任何一种。当然,他指出双语句版本更容易理解是完全正确的。
Python 3.5(PEP 448)允许更好的语法选项:
x = {'a': 1, 'b': 1} y = {'a': 2, 'c': 2} final = {**x, **y} final # {'a': 2, 'b': 1, 'c': 2}
甚至
final = {'a': 1, 'b': 1, **x, **y}
在Python 3.5中,您可以使用unpack ** 为了创建新词典。 这种方法在过去的答案中没有显示出来。此外,它最好使用 {} 代替 dict() 。因为 {} 是一个python文字和 dict() 涉及函数调用。
**
{}
dict()
dict1 = {'a':1} dict2 = {'b':2} new_dict = {**dict1, **dict2} >>>new_dict {'a':1, 'a':2}
在python 3中:
import collections a = {1: 1, 2: 2} b = {2: 3, 3: 4} c = {3: 5} r = dict(collections.ChainMap(a, b, c)) print(r)
日期:
{1: 1, 2: 2, 3: 4}
文档: https://docs.python.org/3/library/collections.html#collections.ChainMap :
我不能使用副本时可以想到的最佳版本是:
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() ,这是由2to3工具自动完成的。
dict(x.items() + y.items())
n = copy(a); n.update(b)
iteritems()
items()
就个人而言,我最喜欢这个版本,因为它在单一功能语法中描述了我想要的东西。唯一的小问题是,从y的值优先于x的值,并没有完全明显,但我不认为很难弄明白。
x = {'a':1, 'b': 2} y = {'b':10, 'c': 11} z = dict(x.items() + y.items()) print z
对于具有两个词典('b')中的键的项目,您可以通过将最后一个放在最后一个来控制哪一个最终出现在输出中。
from collections import Counter dict1 = {'a':1, 'b': 2} dict2 = {'b':10, 'c': 11} result = dict(Counter(dict1) + Counter(dict2))
这应该可以解决您的问题。
我在今天列出的解决方案中遇到的问题是,在合并的字典中,键“b”的值是10,但就我的思维方式而言,它应该是12。 有鉴于此,我提出以下建议:
import timeit n=100000 su = """ x = {'a':1, 'b': 2} y = {'b':10, 'c': 11} """ def timeMerge(f,su,niter): print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f) timeMerge("dict(x, **y)",su,n) timeMerge("x.update(y)",su,n) timeMerge("dict(x.items() + y.items())",su,n) timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n) #confirm for loop adds b entries together x = {'a':1, 'b': 2} y = {'b':10, 'c': 11} for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] print "confirm b elements are added:",x
0.049465 sec for: dict(x, **y) 0.033729 sec for: x.update(y) 0.150380 sec for: dict(x.items() + y.items()) 0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}
(仅适用于Python2.7 *; Python3 *有更简单的解决方案。)
如果您不反对导入标准库模块,则可以这样做
from functools import reduce def merge_dicts(*dicts): return reduce(lambda a, d: a.update(d) or a, dicts, {})
(该 or a 位于 lambda 是必要的,因为 dict.update 总是回来 None 成功。)
or a
dict.update
None