首页 文章

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

提问于
浏览
3502

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

>>> 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() 的最后一次冲突处理也是我正在寻找的 . )

30 回答

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

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

  • 7

    Two dictionaries

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

    n dictionaries

    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/

  • 22

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

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

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

    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的值,并没有完全明显,但我不认为很难弄明白 .

  • 82

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

    对于字典 xyz 成为浅层合并的字典,其中 y 的值替换了 x 中的值 .

    • 在Python 3.5或更高版本中:
    z = {**x, **y}
    
    • 在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 448available as of Python 3.5中提出的新语法是

    z = {**x, **y}
    

    它确实是一个表达式 .

    请注意,我们也可以使用文字符号合并:

    z = {**x, 'foo': 1, 'bar': 2, **y}
    

    现在:

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

    它现在显示为在release schedule for 3.5, PEP 478中实现,现在它已经进入What's New in Python 3.5文档 .

    但是,由于许多组织仍在使用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中使用 . 例如给出dicts ag

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

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

    其他答案的批评

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

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

    在Python 2中,您在内存中为每个dict创建两个列表,在内存中创建第三个列表,其长度等于放在一起的前两个列表的长度,然后丢弃所有三个列表以创建dict . In Python 3, this will fail 因为您要将两个 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 3中使用 items() 的并集(Python 2.7中的 viewitems() )也将失败 . 即使您的值是可以清除的, since sets are semantically unordered, the behavior is undefined in regards to precedence. So don't do this:

    >>> 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构造函数),它's difficult to read, it'不是预期的用法,所以它不是Pythonic .

    以下是remediated in django的用法示例 .

    Dicts旨在采用可清洗密钥(例如frozensets或元组),但 this method fails in Python 3 when keys are not strings.

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

    来自mailing list,该语言的创造者Guido van Rossum写道:

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

    显然dict(x,** y)正在为“调用x.update(y)并返回x”的“酷黑客” . 就个人而言,我发现它比酷酷更卑鄙 .

    我的理解(以及对creator of the language的理解) dict(**y) 的预期用途是为了可读性目的而创建dicts,例如:

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

    代替

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

    对评论的回复

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

    同样,当键是非字符串时,它不适用于3 . 隐式调用 Contract 是命名空间采用普通的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越来越被弃用 .

    {** x,** y}似乎不处理嵌套字典 . 嵌套键的内容只是被覆盖,而不是合并[...]我最终被这些没有递归合并的答案所烧毁,我很惊讶没有人提到它 . 在我对“合并”一词的解释中,这些答案描述了“用另一个更新一个字典”,而不是合并 .

    是 . 我必须回过头来回答一个问题,即在一个表达式中要求 two 词典与第一个's values being overwritten by the second'的浅层合并 .

    假设有两个词典字典,一个可以递归地将它们合并到一个函数中,但是你应该注意不要从任何一个源修改dicts,并且最可靠的方法是在分配值时复制它们 . 由于密钥必须是可清洗的,因此通常是不可变的,因此复制它们是没有意义的:

    from copy import deepcopy
    
    def dict_of_dicts_merge(x, y):
        z = {}
        overlapping_keys = x.keys() & y.keys()
        for key in overlapping_keys:
            z[key] = dict_of_dicts_merge(x[key], y[key])
        for key in x.keys() - overlapping_keys:
            z[key] = deepcopy(x[key])
        for key in y.keys() - overlapping_keys:
            z[key] = deepcopy(y[key])
        return z
    

    用法:

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

    提出其他 Value 类型的意外事件远远超出了这个问题的范围,所以我将在my answer to the canonical question on a "Dictionaries of dictionaries merge"指出 .

    性能较差但正确的Ad-hoc

    这些方法性能较差,但它们将提供正确的行为 . 它们的性能要比 copyupdate 或新解包的性能低得多,因为它们在更高的抽象层次上遍历每个键值对,但它们确实尊重优先顺序(后面的序列优先)

    你也可以在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
    

    字典资源

  • 14

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

    替代:

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

    另一个更简洁的选择:

    z = dict(x, **y)
    

    Note :这已经成为一个流行的答案,但重要的是要指出,如果 y 有任何非字符串键,这一点的工作原理是滥用CPython实现细节,它在Python 3中不起作用,或者在PyPy,IronPython或Jython中 . 另外,Guido is not a fan . 所以我不推荐这种技术用于前向兼容或交叉实现的可移植代码,这实际上意味着它应该完全避免 .

  • 12

    这可以通过单个字典理解来完成:

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

    在我看来,“单一表达”部分的最佳答案是不需要额外的功能,而且很短 .

  • 566

    滥用导致Matthew's answer的单表达式解决方案:

    >>> 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 来绑定一个name和tuples来覆盖lambda的one-expression限制 . 随意畏缩 .

    如果您不关心复制它,您当然也可以这样做:

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

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

    示例如下:

    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给出的深度合并也会产生相同的结果 .

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

  • 175

    是pythonic . 使用comprehension

    z={i:d[i] for d in [x,y] for i in d}
    
    >>> print z
    {'a': 1, 'c': 11, 'b': 10}
    
  • 89

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

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

    这将根据您的需要将最终的字典放在 z 中,并使键 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}
    
  • 7

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

    (仅适用于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 . )

  • 284

    这可能赢得了't be a popular answer, but you almost certainly do not want to do this. If you want a copy that'的合并,然后使用副本(或deepcopy,取决于你想要的),然后更新 . 这两行代码比使用.items().items()的单行创建更具可读性 - 更多Pythonic . 显式优于隐式 .

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

    time而言:

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

  • 28

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

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

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

  • 9

    如果你认为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}
    

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

  • 16

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

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

    在python3中, items 方法no longer returns a list,而是一个视图,它就像一个集合 . 在这种情况下,你需要采用set union,因为与 + 的连接将不起作用:

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

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

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

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

    Edit:

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

    此外,Raymond Hettinger的Chainmap answer非常优雅,因为它可以将任意数量的dicts作为参数,但是from the docs看起来它依次查看每个查找的所有dicts的列表:

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

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

    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的粉丝,但在可能有很多查找的地方看起来不那么实用 .

  • 38

    在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

  • 3771

    使用保留顺序的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}
    
  • 7
    >>> 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}
    
  • 1474

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

    递归/深度更新dict

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

  • 57

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

    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左右 . 不同的词典似乎产生了截然不同的结果,但是_1052223似乎总是会出现 . (如果同一测试的结果不一致,请尝试使用大于默认值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所做的那样,但(并不奇怪)表示法中的差异结果表明不会对性能产生任何可测量的影响 . 使用适合您的任何一种 . 当然,他绝对正确地指出双语句版本更容易理解 .

  • 7
    from collections import Counter
    dict1 = {'a':1, 'b': 2}
    dict2 = {'b':10, 'c': 11}
    result = dict(Counter(dict1) + Counter(dict2))
    

    这应该可以解决您的问题 .

  • 23
    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'}
    
  • 45

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

  • 45

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

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

相关问题