代码之家  ›  专栏  ›  技术社区  ›  Carl Meyer

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

  •  3682
  • Carl Meyer  · 技术社区  · 17 年前

    我有两个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()

    44 回复  |  直到 5 年前
        1
  •  7602
  •   Aaron Hall    5 年前

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

    词典 x y , z 成为一个浅层合并的字典,其中包含来自 Y 十、

    • 在Python 3.9.0或更高版本中(2020年10月17日发布): PEP-584 , discussed here ,并提供了最简单的方法:

      z = x | y          # NOTE: 3.9+ ONLY
      
    • z = {**x, **y}
      
    • 在Python 2中(或3.4或更低版本)编写一个函数:

      def merge_two_dicts(x, y):
          z = x.copy()   # start with keys and values of x
          z.update(y)    # modifies z with keys and values of y
          return z
      

      现在:

      z = merge_two_dicts(x, y)
      

    解释

    假设您有两个词典,并且希望在不更改原始词典的情况下将它们合并到新词典中:

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

    Z )合并值,第二个字典的值覆盖第一个字典的值。

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

    PEP 448 available as of Python 3.5

    这确实是一个单一的表达。

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

    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

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

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

    在这两种方法中, Y 将排在第二位,其值将被替换 十、 因此,中国的价值观 b 将指出 3 在我们的最终结果中。

    单一表达式

    如果您还没有使用Python3.5或需要编写向后兼容的代码,并且希望在 单一表达式

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

    然后有一个表达式:

    z=合并两个指令(x,y)
    

    您还可以创建一个函数来合并任意数量的字典,从零到非常大的数字:

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

    a g :

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

    G 将优先于字典 A. f 等等

    对其他答案的评论

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

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

    在Python2中,为每个dict在内存中创建两个列表,在内存中创建第三个列表,其长度等于前两个列表的长度,然后丢弃所有三个列表以创建dict。 在Python3中,这将失败 因为你加了两个 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())) . 这是对资源和计算能力的浪费。

    items() 在Python 3中( viewitems() 由于集合在语义上是无序的,因此行为在优先级方面是未定义的。所以不要这样做:

    >>> 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 = {'a': 2}
    >>> y = {'a': 1}
    >>> dict(x.items() | y.items())
    {'a': 2}
    

    z = dict(x, **y)
    

    这使用 dict

    下面是一个用法的示例 remediated in django .

    字典用于获取哈希键(例如。 frozenset s或元组),但是 在Python3中,当键不是字符串时,此方法失败。

    >>> 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写道:

    我同意

    x、 更新(y)并返回x”。就个人而言,我觉得这比 凉的

    这是我的理解(以及对 creator of the language )这是它的预期用途 dict(**y)

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

    而不是

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

    对评论的答复

    dict(x, **y) 符合dict规范,顺便说一句,该规范适用于Python2和Python3。这只适用于字符串键的事实是关键字参数如何工作的直接结果,而不是dict的短期结果。在这里使用**运算符也不是滥用机制,事实上,**的设计正是为了将字典作为关键字传递。

    同样,当键不是字符串时,它不适用于3。隐式调用约定是名称空间采用普通字典,而用户只能传递字符串形式的关键字参数。所有其他Callable都强制执行它。 字典

    >>> 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),这种不一致性是很糟糕的。因此,它在Python3中得到了修复,因为这种用法可能是一个突破性的变化。

    我认为,故意编写只在一种语言的一个版本中工作或只在特定的任意约束下工作的代码是恶意的无能。

    更多评论:

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

    我的答复是: merge_two_dicts(x, y) 事实上,如果我们真的关心可读性的话,对我来说似乎更清楚。而且它不具有前向兼容性,因为Python2越来越不受欢迎。

    {**x, **y}

    对我必须让你回到这个问题上来,这个问题要求你回答一个问题 浅的 合并

    假设有两个字典,其中一个可能会在单个函数中递归地合并它们,但是您应该小心不要修改来自任何一个源的字典,避免这种情况的最可靠的方法是在赋值时创建一个副本。由于键必须是可散列的,因此通常是不可变的,因此复制它们是毫无意义的:

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

    my answer to the canonical question on a "Dictionaries of dictionaries merge" .

    性能较差但正确的Ad HOC

    他们会的 性能优于 copy update 或者新的解包,因为它们在更高的抽象级别上迭代每个键值对,但是 遵守优先顺序(后面的词典优先)

    您还可以手动将字典链接到 dict comprehension :

    {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()) # iteritems in Python 2
    

    itertools.chain

    from itertools import chain
    z = dict(chain(x.items(), y.items())) # iteritems in Python 2
    

    性能分析

    我只想对已知行为正确的用法进行性能分析。(独立,因此您可以自己复制和粘贴。)

    from timeit import repeat
    from itertools import chain
    
    x = dict.fromkeys('abcdefg')
    y = dict.fromkeys('efghijk')
    
    def merge_two_dicts(x, y):
        z = x.copy()
        z.update(y)
        return z
    
    min(repeat(lambda: {**x, **y}))
    min(repeat(lambda: merge_two_dicts(x, y)))
    min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()}))
    min(repeat(lambda: dict(chain(x.items(), y.items()))))
    min(repeat(lambda: dict(item for d in (x, y) for item in d.items())))
    

    在Python 3.8.1中,NixOS:

    >>> min(repeat(lambda: {**x, **y}))
    1.0804965235292912
    >>> min(repeat(lambda: merge_two_dicts(x, y)))
    1.636518670246005
    >>> min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()}))
    3.1779992282390594
    >>> min(repeat(lambda: dict(chain(x.items(), y.items()))))
    2.740647904574871
    >>> min(repeat(lambda: dict(item for d in (x, y) for item in d.items())))
    4.266070580109954
    
    $ uname -a
    Linux nixos 4.19.113 #1-NixOS SMP Wed Mar 25 07:06:15 UTC 2020 x86_64 GNU/Linux
    

        2
  •  1717
  •   Nikita Vlasenko    5 年前

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

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

    这将,正如你所希望的那样,把最后的格言放进去 z ,并为键设置值 b 被第二个命令正确覆盖( y )迪克特价值观:

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

    如果使用Python2,甚至可以删除 list() 电话。要创建z,请执行以下操作:

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

    x = {'a':1, 'b': 2}
    y = {'b':10, 'c': 11}
    z = x | y
    print(z)
    
    {'a': 1, 'c': 11, 'b': 10}
    
        3
  •  697
  •   Matthew Schinckel    17 年前

    另一种选择:

    z = x.copy()
    z.update(y)
    
        4
  •  396
  •   Carl Meyer    10 年前

    z = dict(x, **y)
    

    笔记 :这已成为一个流行的答案,但重要的是要指出,如果 y Guido is not a fan . 因此,我不能将这种技术推荐给前向兼容或跨实现的可移植代码,这实际上意味着应该完全避免使用这种技术。

        5
  •  248
  •   twasbrillig    11 年前

    这可能不是一个流行的答案,但您几乎肯定不想这样做。如果需要合并的副本,请使用copy(或 deepcopy

    此外,当您使用.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
    

    在我看来,由于可读性,前两者之间的微小减速是值得的。此外,用于创建字典的关键字参数仅在Python2.3中添加,而copy()和update()将在旧版本中使用。

        6
  •  181
  •   the Tin Man    11 年前

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

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

    z2 不仅更短、更简单,而且速度更快。您可以使用 timeit Python附带的模块。

    示例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的数字。)

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

    让我得出这样的结论 z3 比以前快了一点 z1 ,但速度不如 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
    

    换句话说,, z0 z2 似乎有着本质上相同的表现。你认为这可能是巧合吗?我不。。。。

    事实上,我甚至认为纯Python代码不可能做得比这更好。如果您可以在C扩展模块中做得更好,我想Python人员可能会对将您的代码(或方法的变体)合并到Python核心中感兴趣。Python使用 dict 在很多地方;优化其运营是一件大事。

    你也可以这样写

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

        7
  •  168
  •   Raymond Hettinger    5 年前

    在Python 3.0及更高版本中 collections.ChainMap 将多个DICT或其他映射组合在一起以创建一个可更新的视图:

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

    :你可以使用 PEP 448 扩展字典打包和解包。这既快又简单:

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

    Python 3.9及更高版本的更新 :您可以使用 PEP 584 工会经营者:

    >>> x = {'a':1, 'b': 2}
    >>> y = {'b':10, 'c': 11}
    >>> x | y
    {'a': 1, 'b': 10, 'c': 11}
    
        8
  •  145
  •   Rainy rcreswick    11 年前

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

    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
    
        9
  •  115
  •   Dawid Gosławski    10 年前

    递归/深度更新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的编辑。

        10
  •  92
  •   Bilal Syed Hussain    6 年前

    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}
    

    在Python3.9中,还将|和|=与下面的PEP 584示例一起使用

    d = {'spam': 1, 'eggs': 2, 'cheese': 3}
    e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
    d | e
    # {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}
    
        11
  •  89
  •   driax    15 年前

    在不使用copy的情况下,我认为最好的版本是:

    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上。如果您进行更改,此版本也适用于Python3 iteritems() items()

    就我个人而言,我最喜欢这个版本,因为它用一个函数语法很好地描述了我想要的东西。唯一的一个小问题是,它并没有完全清楚地表明y的值优先于x的值,但我认为这并不难理解。

        12
  •  85
  •   Greg Hewgill    17 年前
    x = {'a':1, 'b': 2}
    y = {'b':10, 'c': 11}
    z = dict(x.items() + y.items())
    print z
    

    对于两个字典('b')中都有键的项,您可以通过最后一个来控制输出中的键。

        13
  •  64
  •   phobie    14 年前

    这个问题已经回答了好几次了,, 这个问题的简单解决方案尚未列出。

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

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

        14
  •  60
  •   Sam Watkins    13 年前
    def dict_merge(a, b):
      c = a.copy()
      c.update(b)
      return c
    
    new = dict_merge(old, extras)
    

    在这些阴暗而可疑的答案中,这个光辉的例子是唯一一个用Python合并dicts的好方法,它得到了独裁者终身支持 吉多·范罗苏姆 他自己其他人提出了其中的一半,但没有将其放入函数中。

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

    {'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}
    
        15
  •  53
  •   EMS    14 年前

    如果你认为兰姆达斯是邪恶的,那就不要再读下去了。

    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
  •  46
  •   Robino    9 年前

    comprehension :

    z={i:d[i] for d in [x,y] for i in d}
    
    >>> print z
    {'a': 1, 'c': 11, 'b': 10}
    
        17
  •  41
  •   Community Mohan Dere    9 年前

    在python3中 items no longer returns a list 而是一个 看法 ,其作用类似于一个集合。在这种情况下,需要使用集合并集,因为它与 + 不起作用:

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

    viewitems 方法应该代替 项目 :

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

    无论如何,我更喜欢这种表示法,因为将其视为集合并集操作而不是串联(如标题所示)似乎更为自然。

    编辑:

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

    answer 非常优雅,因为它可以接受任意数量的dict作为参数,但是 from the docs 它看起来像是按顺序查找每个查找的所有DICT的列表:

    查找连续搜索基础映射,直到找到键。

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

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

        18
  •  32
  •   Mathieu Larose    9 年前

    两本字典

    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/

        19
  •  31
  •   reubano    5 年前

    # py2
    from itertools import chain, imap
    merge = lambda *args: dict(chain.from_iterable(imap(dict.iteritems, args)))
    
    # py3
    from itertools import chain
    merge = lambda *args: dict(chain.from_iterable(map(dict.items, 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}
    
        20
  •  29
  •   Community Mohan Dere    9 年前

    滥用导致一个表达式的解决方案 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 绑定名称和元组以覆盖lambda的一个表达式限制。别客气。

    >>> x = {'a':1, 'b': 2}
    >>> y = {'b':10, 'c': 11}
    >>> z = (x.update(y), x)[1]
    >>> z
    {'a': 1, 'b': 10, 'c': 11}
    
        21
  •  24
  •   Thanh Lim    13 年前

    尽管答案对这个很有帮助 浅的 dictionary,这里定义的所有方法实际上都没有进行深度dictionary合并。

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

    如果“一”条目确实是一个合并条目,那么它的字典中应该有“depth_2”和“extra”作为条目。

    同时使用链也不起作用:

    from itertools import chain
    print dict(chain(a.iteritems(), b.iteritems()))
    

    结果:

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

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

    是的,合并示例词典是可行的,但它们都不是通用的合并机制。一旦我编写了一个进行真正合并的方法,我将在以后更新它。

        22
  •  22
  •   gilch    5 年前

    如果你不介意变异的话 x

    x.update(y) or x
    

    知道 update() 总是回来 None 十、

    标准库中的大多数变异方法(如 .update() )返回 按照惯例,这种模式也适用于这些。但是,如果您使用的是dict子类或其他不遵循此约定的方法,那么 or 可能返回其左操作数,这可能不是您想要的。相反,您可以使用元组显示和索引,无论第一个元素的计算结果如何(尽管它没有那么漂亮):

    (x.update(y), x)[-1]
    

    如果你没有 十、 lambda 在不使用赋值语句的情况下生成局部变量。这相当于使用 兰姆达 作为一个 ,这是函数式语言中的一种常见技术,但可能是非语法的。

    (lambda x: x.update(y) or x)({'a': 1, 'b': 2})
    

    尽管它与下面使用的新walrus操作符(仅限Python 3.8+)没有太大区别,

    (x := {'a': 1, 'b': 2}).update(y) or x
    

    (lambda x={'a': 1, 'b': 2}: x.update(y) or x)()
    

    如果你真的想要一份, PEP 584 风格 x | y PEP 448 {**x, **y} 对于3.5+来说是最简单的。但是,如果您的(甚至更旧的)Python版本中没有这种功能,那么 让表达 这种模式在这里也适用。

    (lambda z=x.copy(): z.update(y) or z)()
    

    (z := x.copy()).update(y) or z ,但如果您的Python版本足够新,那么就可以使用PEP448样式。)

        23
  •  18
  •   xjcl    5 年前

    New 在Python 3.9中: 使用联合运算符( | )合并 dict set s:

    >>> d = {'a': 1, 'b': 2}
    >>> e = {'a': 9, 'c': 3}
    >>> d | e
    {'a': 9, 'b': 2, 'c': 3}
    

    对于匹配的键 字典 优先 .

    这也适用于 |= 修改 字典 到位:

    >>> e |= d    # e = e | d
    >>> e
    {'a': 1, 'c': 3, 'b': 2}
    
        24
  •  17
  •   GetFree    12 年前

    .update 不返回任何内容。
    我只是使用一个简单的助手函数来解决这个问题:

    def merge(dict1,*dicts):
        for dict2 in dicts:
            dict1.update(dict2)
        return dict1
    

    示例:

    merge(dict1,dict2)
    merge(dict1,dict2,dict3)
    merge(dict1,dict2,dict3,dict4)
    merge({},dict1,dict2)  # this one returns a new copy
    
        25
  •  17
  •   kjo    10 年前

    (仅适用于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 (关于成功)

        26
  •  16
  •   Bijou Trouvaille    13 年前

    根据这里和其他地方的想法,我理解了一个函数:

    def merge(*dicts, **kv): 
          return { k:v for d in list(dicts) + [kv] for k,v in d.items() }
    

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

        27
  •  16
  •   upandacross    12 年前

    到目前为止,我所列出的解决方案的问题是,在合并字典中,键“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}
    
        28
  •  15
  •   reetesh11    10 年前
    from collections import Counter
    dict1 = {'a':1, 'b': 2}
    dict2 = {'b':10, 'c': 11}
    result = dict(Counter(dict1) + Counter(dict2))
    

    这应该能解决你的问题。

        29
  •  15
  •   ShadowRanger    7 年前

    scheduled for 20 October, 2019 PEP 572: Assignment Expressions . 新的赋值表达式运算符 := 允许您分配测试结果 copy 还是用它来打电话 update ,将组合代码保留为单个表达式,而不是两个语句,更改:

    newdict = dict1.copy()
    newdict.update(dict2)
    

    致:

    (newdict := dict1.copy()).update(dict2)
    

    dict (您要求一个表达式返回 字典 ; 以上内容创建并分配给 newdict myfunc((newdict := dict1.copy()).update(dict2)) ),然后只需添加 or newdict 到底 使现代化 None ,这是falsy,然后它将求值并返回 纽迪克特 作为表达式的结果):

    (newdict := dict1.copy()).update(dict2) or newdict
    

    总的来说,我不鼓励这种方法,而是赞成:

    newdict = {**dict1, **dict2}
    

    which you should ),根本不需要结果的名称(因此,在构造立即传递给函数或包含在 list / tuple (在CPython上)大致相当于:

    newdict = {}
    newdict.update(dict1)
    newdict.update(dict2)
    

    但在C层,使用混凝土 字典 API,因此不涉及动态方法查找/绑定或函数调用调度开销(其中 (newdict := dict1.copy()).update(dict2) 在行为上不可避免地与最初的两行程序相同,以离散的步骤执行工作,方法的动态查找/绑定/调用。

    它还具有更大的扩展性,如合并三个 字典 这是显而易见的:

     newdict = {**dict1, **dict2, **dict3}
    

    使用赋值表达式不会像这样缩放;你能得到的最接近的答案是:

     (newdict := dict1.copy()).update(dict2), newdict.update(dict3)
    

    或者没有 没有一个 没有一个 结果:

     (newdict := dict1.copy()).update(dict2) or newdict.update(dict3)
    

    其中任何一个都显然要丑陋得多,并包括进一步的低效率(要么是浪费的临时资源) 元组 属于 没有一个 s表示逗号分隔,或无意义的真实性测试 使现代化 没有一个 回报 or

    赋值表达式方法的唯一真正优势在于:

    1. 您有需要同时处理这两个问题的通用代码 set s和 字典 (他们都支持 复制 ,因此代码的工作原理与您预期的大致相同)
    2. 您希望接收任意类似dict的对象 ,不只是 它本身 并且必须保留左侧的类型和语义 (而不是以一个简单的 字典 ).一会儿 myspecialdict({**speciala, **specialb}) 字典 ,如果 myspecialdict 特色鲜明 字典 不能保存(例如,常规 字典 s现在基于键的第一次外观保留顺序,并基于键的最后一次外观保留值;您可能需要一个基于 键的外观(因此更新值也会将其移动到末尾),则语义将是错误的。由于赋值表达式版本使用命名方法(可能会重载这些方法以适当地进行操作),因此它从不创建 字典 完全(除非 dict1 已经是一个 ),保留原始类型(以及原始类型的语义),同时避免任何临时性。
        30
  •  12
  •   RemcoGerlich    11 年前

    这可以通过一次听写理解来完成:

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