提问



我有两个Python字典,我想编写一个返回这两个字典的表达式,合并。 update()方法将是我需要的,如果它返回其结果而不是就地修改字典。


>>> 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而不是x中获得最终合并的字典?


(要清楚的是,dict.update()的最后一次胜利冲突处理也是我正在寻找的。)

最佳参考



  

如何在一个表达式中合并两个Python词典?




对于字典xyz成为一个合并字典,其中y的值代替x中的值。



  • 在Python 3.5或更高版本中,:


    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    

  • 在Python 2中,(或3.4或更低版本)编写一个函数:


    def merge_two_dicts(x, y):
        z = x.copy()   # start with x's keys and values
        z.update(y)    # modifies z with y's keys and values & returns None
        return z
    





    z = merge_two_dicts(x, y)
    



说明



假设您有两个dicts,并且您希望将它们合并到一个新的dict而不更改原始的dicts:


x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}


期望的结果是获得一个新的字典(z),其值合并,第二个字典的值覆盖第一个。


>>> z
{'a': 1, 'b': 3, 'c': 4}


在PEP 448中提出并且从Python 3.5开始提供的新语法是[161] [162]


z = {**x, **y}


它确实是一个表达式。它现在显示为在3.5,PEP 478的发布时间表中实现,现在它已经进入了Python 3.5文档中的新功能。[163] [164]


但是,由于许多组织仍在使用Python 2,因此您可能希望以向后兼容的方式执行此操作。 Python 2和Python 3.0-3.4中提供的经典Pythonic方法是通过两个步骤完成的:


z = x.copy()
z.update(y) # which returns None since it mutates z


在这两种方法中,y将成为第二,其值将取代x的值,因此'b'将在我们的最终结果中指向3


尚未在Python 3.5上,但需要单个表达式



如果您尚未使用Python 3.5,或者需要编写向后兼容的代码,并且您希望在单个表达式中使用它,那么最正确的方法是将它放在函数中:


def merge_two_dicts(x, y):
    """Given two dicts, merge them into a new dict as a shallow copy."""
    z = x.copy()
    z.update(y)
    return z


然后你有一个表达式:


z = merge_two_dicts(x, y)


您还可以创建一个函数来合并未定义数量的dicts,从零到非常大的数字:


def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result


对于所有dicts,此函数将在Python 2和3中使用。例如鉴于ag:


z = merge_dicts(a, b, c, d, e, f, g) 


g中的键值对优先于dicts af,依此类推。


对其他答案的批评



不要使用你在之前接受的答案中看到的内容:


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


在Python 2中,您在内存中为每个dict创建两个列表,在内存中创建第三个列表,其长度等于放在一起的前两个列表的长度,然后丢弃所有三个列表以创建dict。 在Python 3中,这将失败因为您将两个dict_items对象添加到一起,而不是两个列表 -


>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'


你必须明确地将它们创建为列表,例如z = dict(list(x.items()) + list(y.items()))。这是浪费资源和计算能力。


类似地,当Python是不可用的对象(例如列表)时,在Python 3(viewitems()中使用items()的并集也将失败)。即使您的值是可清除的,因为集合在语义上是无序的,所以行为在优先级方面是未定义的。所以不要这样做:


>>> c = dict(a.items() | b.items())


此示例演示了值不可用时会发生什么:


>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'


这里是一个y应该具有优先权的例子,但是由于集合的任意顺序,x的值被保留:


>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}


另一个黑客你不应该使用:


z = dict(x, **y)


这使用了dict构造函数,并且速度非常快且内存效率高(甚至比我们的两步过程稍微多一些),但除非你确切地知道这里发生了什么(也就是说,第二个dict被传递为dict构造函数的关键字参数),它很难阅读,它不是预期的用法,所以它不是Pythonic。


这是django中修复用法的一个例子。[165]


Dicts旨在获取可清除密钥(例如frozensets或tuples),但当密钥不是字符串时,此方法在Python 3中失败。


>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings


来自邮件列表的语言创建者Guido van Rossum写道:[166]



  我很好
  宣布dict({},** {1:3})是非法的,因为它毕竟是滥用
  **机制。







  显然dict(x,** y)就像电话的酷黑客一样
  x.update(y)并返回x。我个人认为它比它更卑鄙
  凉。



我的理解(以及对语言创建者的理解)dict(**y)的预期用途是为了可读性目的而创建词条,例如:[167]


dict(a=1, b=10, c=11)


代替


{'a': 1, 'b': 10, 'c': 11}


对评论的回应




  尽管Guido说,dict(x, **y)符合dict规范,但顺便说一下。适用于Python 2和3.事实上,这仅适用于字符串键,这是关键字参数如何工作而不是dict短路的直接结果。在这个地方也没有使用**运算符滥用该机制,事实上**的设计恰恰是为了将dicts作为关键字传递。



同样,当密钥是非字符串时,它不适用于3。隐式调用契约是命名空间采用普通的dicts,而用户必须只传递字符串的关键字参数。所有其他callables强制执行它。dict破坏了Python 2中的这种一致性:


>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}


鉴于Python的其他实现(Pypy,Jython,IronPython),这种不一致性很糟糕。因此它在Python 3中得到了修复,因为这种用法可能是一个突破性的变化。


我向你提出,故意编写只能在一种语言版本中工作的代码或仅在某些任意约束条件下工作的代码是恶意无能的。


另一条评论:



  dict(x.items() + y.items())仍然是Python 2最易读的解决方案。可读性很重要。



我的回答:merge_two_dicts(x, y)对我来说实际上似乎更清楚,如果我们真的关心可读性。而且它不向前兼容,因为Python 2越来越被弃用。


性能较差但正确的Ad-hocs



这些方法性能较差,但它们会提供正确的行为。
copyupdate或新解包相比,它们更少性能,因为它们在更高的抽象级别迭代每个键值对,但是它们 尊重优先顺序(后面的dicts优先)


你也可以在dict理解中手动链接dicts:


{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7


或者在python 2.6中(当引入生成器表达式时可能早在2.4):


dict((k, v) for d in dicts for k, v in d.items())


itertools.chain将以正确的顺序将迭代器链接到键值对:


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


绩效分析



我只会对已知行为正确的用法进行性能分析。


import timeit


以下是在Ubuntu 14.04上完成的


在Python 2.7(系统Python)中:


>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934


在Python 3.5(deadsnakes PPA)中:


>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287


词典上的资源




  • 我对Python的字典实现的解释,更新为3.6。

  • 关于如何向字典添加新密钥的答案

  • 将两个列表映射到字典

  • 关于字典的官方Python文档

  • 字典甚至更强 - 在2017年的Pycon上由Brandon Rhodes讲话

  • 现代Python词典,一个伟大创意的汇合 - Raymond Hettinger在2017年Pycon上的讲话


其它参考1


在您的情况下,您可以做的是: [171] [172] [173]


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


这将根据您的需要将最终的dict放在z中,并使key b的值被第二个(y)dict的值正确覆盖:


>>> 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}

其它参考2


替代:


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

其它参考3


另一个更简洁的选择:


z = dict(x, **y)


注意:这已经成为一个受欢迎的答案,但重要的是要指出,如果y有任何非字符串键,这一点的作用就是滥用CPython实现细节,它在Python 3或PyPy,IronPython或Jython中不起作用。此外,Guido不是粉丝。所以我不推荐这种技术用于前向兼容或交叉实现的可移植代码,这实际上意味着它应该完全避免。[174]

其它参考4


这可能不是一个受欢迎的答案,但你几乎肯定不想这样做。如果你想要一个合并的副本,那么使用副本(或深度复制,取决于你想要的),然后更新。这两行代码比使用.items()+ .items()的单行创建更具可读性 - 更多Pythonic。明确比隐含更好。[175]


另外,当你使用.items()(Python 3.0之前的版本)时,你会创建一个包含dict项目的新列表。如果你的字典很大,那么这就是很多开销(两个大的列表,一旦创建了合并的dict,它就会被丢弃。)update()可以更有效地工作,因为它可以逐项运行第二个dict。


就时间而言:[176]


>>> 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()将在旧版本中使用。

其它参考5


在后续回答中,您询问了这两种备选方案的相对表现:


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


在我的机器上,至少(相当普通的x86_64运行Python 2.5.2),替代z2不仅更短更简单,而且速度更快。您可以使用Python附带的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:非重叠字典将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()))


一些快速测试,例如


% 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或y,我将复制x而不是就地修改它,如下所示:


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所做的那样,但(并不奇怪)记谱法的差异原来并没有对性能产生任何可衡量的影响。使用适合您的任何一种。当然,他指出双语句版本更容易理解是完全正确的。

其它参考6


我想要类似的东西,但是能够指定复制键上的值是如何合并的,所以我将其解决了(但没有对它进行大量测试)。显然这不是单个表达式,而是单个函数调用。


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

其它参考7


在Python 3中,您可以使用 collections.ChainMap 将多个dicts或其他映射组合在一起以创建单个可更新视图:[177]


>>> 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

其它参考8


递归/深度更新字典



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的编辑。

其它参考9


我在不使用副本时可以想到的最佳版本是:


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上如此。如果你将iteritems()改为items(),这个版本在Python 3中也有效,这是由2to3工具自动完成。


就个人而言,我最喜欢这个版本,因为它在单一功能语法中描述了我想要的东西。唯一的小问题是,它并没有完全明显y的值优先于x的值,但我不相信很难弄明白。

其它参考10


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}

其它参考11


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


对于在两个词典(b)中都带有键的项目,您可以通过将最后一个键入最后一个来控制哪一个最终出现在输出中。

其它参考12


虽然问题已经多次回答,
这个问题的简单解决方案尚未列出。


x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)


它与z0和上面提到的邪恶z2一样快,但易于理解和改变。

其它参考13


def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)


在这些阴暗和可疑的答案中,这个光辉的例子是在Python中合并dicts的唯一好方法,由独裁者终身认可 Guido van Rossum 自己!其他人建议这一半,但没有把它放在一个功能。


print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})


得到:


{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

其它参考14


如果你认为lambdas是邪恶的,那就不要再读了。
根据要求,您可以使用一个表达式编写快速且内存有效的解决方案:


x = {'a':1, 'b':2}
y = {'b':10, 'c':11}
z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y)
print z
{'a': 1, 'c': 11, 'b': 10}
print x
{'a': 1, 'b': 2}


如上所述,使用两行或编写函数可能是更好的方法。

其它参考15


在python3中,items方法不再返回列表,而是返回视图,其作用类似于集合。在这种情况下,你需要采用集合联合,因为与+连接起来不会起作用:[178]


dict(x.items() | y.items())


对于2.7版中类似python3的行为,viewitems方法应该代替items:


dict(x.viewitems() | y.viewitems())


不管怎样我更喜欢这种符号,因为将它看作是一个联合操作而不是连接似乎更自然(如标题所示)。





python 3还有几点。首先,请注意dict(x, **y)技巧在python 3中不起作用,除非y中的键是字符串。


此外,Raymond Hettinger的Chainmap答案非常优雅,因为它可以使用任意数量的dicts作为参数,但是从文档看起来它依次查看每个查找的所有dicts的列表: [180]



  查找会连续搜索基础映射,直到找到密钥。



如果您的应用程序中有大量查找,这会降低您的速度:


In [1]: from collections import ChainMap
In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo))
In [3]: chainmap_dict = ChainMap(y, x)
In [4]: union_dict = dict(x.items() | y.items())
In [5]: timeit for k in union_dict: union_dict[k]
100000 loops, best of 3: 2.15 µs per loop
In [6]: timeit for k in chainmap_dict: chainmap_dict[k]
10000 loops, best of 3: 27.1 µs per loop


所以查找速度要慢一个数量级。我是Chainmap的粉丝,但在可能有很多查找的地方看起来不那么实用。

其它参考16


是pythonic。使用理解:[181]


z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}

其它参考17


滥用导致马修回答的单表达式解决方案:


>>> 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的一个表达式限制。随意畏缩。


如果你不在乎复制它,你当然可以这样做:


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

其它参考18


使用保留顺序的itertools的简单解决方案(后面的dicts优先)


import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))


它的用法:


>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> merge(x, y)
{'a': 1, 'b': 10, 'c': 11}

>>> z = {'c': 3, 'd': 4}
>>> merge(x, y, z)
{'a': 1, 'b': 10, 'c': 3, 'd': 4}

其它参考19


两本词典


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))


sum表现不佳。见https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/[183]​​]]

其它参考20


尽管这个浅字典的答案很好,但这里定义的方法实际上都没有进行深层字典合并。


示例如下:


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()))


结果是:


{'two': True, 'one': {'extra': False}}


rcwesick给出的深度合并也会产生相同的结果。


是的,它可以合并样本字典,但它们都不是合并的通用机制。一旦我编写了一个真正合并的方法,我将在稍后更新。

其它参考21


在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:[[[184]

其它参考22


>>> 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}

其它参考23


对于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}

其它参考24


借鉴这里和其他地方的想法,我理解了一个功能:


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代替。

其它参考25


我在今天列出的解决方案中遇到的问题是,在合并的字典中,键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}

其它参考26


(仅适用于Python2.7 *; Python3 *有更简单的解决方案。)


如果你不反对导入标准库模块,你可以这样做


from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})


(lambda中的or a位是必要的,因为dict.update成功时总是返回None。)

其它参考27


在Python 3.5中,您可以使用unpack **来创建新的字典。
这种方法在过去的答案中没有显示出来。另外,最好使用{}而不是dict()。因为{}是python文字而dict()涉及函数调用。


dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}

其它参考28


您可以使用toolz.merge([x, y])[185]

其它参考29


使用词典理解,你可以


x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}

dc = {xi:(x[xi] if xi not in list(y.keys()) 
           else y[xi]) for xi in list(x.keys())+(list(y.keys()))}





>>> dc
{'a': 1, 'c': 11, 'b': 10}


请注意理解中if else的语法


{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }