6165

我有两个 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()也是我正在寻找的。)

4

56 回答 56

7877

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

对于字典xyz变成浅合并字典,其值来自y替换来自的值x

  • 在 Python 3.9.0 或更高版本(2020 年 10 月 17 日发布)中:此处讨论的 PEP-584已实现并提供了最简单的方法:

    z = x | y          # NOTE: 3.9+ ONLY
    
  • 在 Python 3.5 或更高版本中:

    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中提出并从 Python 3.5 开始提供的新语法是

z = {**x, **y}

它确实是一个单一的表达方式。

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

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

现在:

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

它现在显示为在3.5 的发布计划 PEP 478中实现,并且它现在已进入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's 值,因此b将指向3我们的最终结果。

还没有在 Python 3.5 上,但想要一个表达式

如果您还没有使用 Python 3.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 = merge_two_dicts(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

此函数适用于所有字典的 Python 2 和 3。a例如给字典g

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

和 in 的键值对g将优先于ato的字典f,依此类推。

对其他答案的批评

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

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())). 这是对资源和计算能力的浪费。

同样,当值是不可散列的对象(例如列表)时,items()在 Python 3 中(在 Python 2.7 中)采用并集也会失败。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由于集合的任意顺序,保留了 from 的值:

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

您不应该使用的另一个技巧:

z = dict(x, **y)

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

这是在 django中修复的用法示例。

字典旨在采用可散列的键(例如frozensets 或元组),但是当键不是字符串时,此方法在 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在邮件列表中写道:

我可以宣布 dict({}, **{1:3}) 非法,因为毕竟这是对 ** 机制的滥用。

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

我的理解(以及语言创建者的理解)的预期用途dict(**y)是为了可读性目的创建字典,例如:

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

代替

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

对评论的回应

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

同样,当键不是字符串时,它不适用于 3。隐式调用约定是命名空间采用普通字典,而用户必须只传递字符串形式的关键字参数。所有其他可调用对象都强制执行它。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}似乎不处理嵌套字典。嵌套键的内容只是被覆盖,而不是合并[...]我最终被这些不递归合并的答案烧毁了,我很惊讶没有人提到它。在我对“合并”一词的解释中,这些答案描述了“用另一个更新一个字典”,而不是合并。

是的。我必须让你回到这个问题,它要求对两个字典进行浅层合并,第一个的值被第二个的值覆盖 - 在一个表达式中。

假设有两个字典,一个可能会递归地将它们合并到一个函数中,但是您应该注意不要从任一源修改字典,避免这种情况的最可靠方法是在分配值时进行复制。由于键必须是可散列的,因此通常是不可变的,因此复制它们是没有意义的:

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

提出其他值类型的意外情况远远超出了这个问题的范围,所以我将指出我对“字典合并字典”的规范问题的回答

性能较差但正确的 Ad-hoc

这些方法的性能较差,但它们会提供正确的行为。它们的性能将远低于copyandupdate或新的解包,因为它们在更高的抽象级别上迭代每个键值对,但它们确实尊重优先顺序(后面的字典具有优先权)

您还可以在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

字典资源

于 2014-11-10T22:11:48.750 回答
1734

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

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

这将如您所愿,将最终的 dict 放入z,并使 key 的值b被第二个 ( y) dict 的值正确覆盖:

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

如果您使用 Python 2,您甚至可以删除list()调用。要创建 z:

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

如果你使用 Python 3.9.0a4 或更高版本,那么你可以直接使用:

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = x | y
print(z)
{'a': 1, 'c': 11, 'b': 10}
于 2008-09-02T07:50:12.600 回答
705

替代:

z = x.copy()
z.update(y)
于 2008-09-02T13:00:46.707 回答
404

另一个更简洁的选项:

z = dict(x, **y)

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

于 2008-09-02T15:52:07.777 回答
249

这可能不会是一个流行的答案,但你几乎肯定不想这样做。如果您想要一个合并的副本,请使用 copy (或deepcopy,取决于您想要什么)然后更新。这两行代码比使用 .items() + .items() 创建的单行代码更具可读性 - 更 Pythonic。显式优于隐式。

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

时间方面:

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

于 2008-09-08T11:16:54.330 回答
183

在后续回答中,您询问了这两种替代方案的相对性能:

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

至少在我的机器上(运行 Python 2.5.2 的相当普通的 x86_64),替代方案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比. 绝对不值得所有额外的打字。z1z2

这个讨论仍然缺少一些重要的东西,即这些替代方案与合并两个列表的“明显”方式的性能比较:使用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 核心感兴趣。Pythondict在很多地方使用;优化其运营是一件大事。

你也可以这样写

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

正如托尼所做的那样,但(并不奇怪)符号的差异对性能没有任何可衡量的影响。使用任何适合您的方式。当然,他完全正确地指出,两语句版本更容易理解。

于 2008-10-23T02:38:56.143 回答
174

在 Python 3.0 及更高版本中,您可以使用collections.ChainMapwhich 将多个 dicts 或其他映射组合在一起来创建单个可更新的视图:

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

Python 3.5 及更高版本的更新:您可以使用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}
于 2013-04-28T03:15:38.467 回答
148

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

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
于 2008-09-04T19:08:25.870 回答
117

递归/深度更新字典

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

感谢 redna 的编辑。

于 2011-11-29T11:52:15.537 回答
94

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.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'}
于 2015-02-26T21:27:52.280 回答
91

在不使用副本的情况下,我能想到的最佳版本是:

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

至少在 CPython 上,它比 快,dict(x.items() + y.items())但不如 快。n = copy(a); n.update(b)如果您更改iteritems()为,此版本也适用于 Python 3 items(),这是由 2to3 工具自动完成的。

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

于 2010-10-14T18:55:15.583 回答
89
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

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

于 2008-09-02T07:49:27.360 回答
67

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

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

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

于 2011-10-14T16:12:33.527 回答
62
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'}
于 2012-08-06T09:24:44.800 回答
55

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

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

于 2011-11-23T18:08:23.420 回答
48

是pythonic。使用理解

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

>>> print z
{'a': 1, 'c': 11, 'b': 10}
于 2016-01-20T11:46:22.490 回答
42

在 python3 中,该items方法不再返回一个 list,而是一个view,它的作用就像一个集合。在这种情况下,您需要使用 set union ,因为连接 with+不起作用:

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 列表:

查找连续搜索底层映射,直到找到一个键。

如果您的应用程序中有很多查找,这可能会减慢您的速度:

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

于 2013-10-09T18:09:08.627 回答
33

两本词典

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

词典_

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/

于 2012-10-17T02:09:45.660 回答
32

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

# 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}
于 2015-08-04T14:54:58.017 回答
30

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

>>> 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}
于 2013-08-07T21:23:08.817 回答
28

我用perfplot对建议进行了基准测试,发现旧的

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

与新的(Python 3.9+)一起是最快的解决方案

x | y

在此处输入图像描述


重现情节的代码:

from collections import ChainMap
from itertools import chain
import perfplot


def setup(n):
    x = dict(zip(range(n), range(n)))
    y = dict(zip(range(n, 2 * n), range(n, 2 * n)))
    return x, y


def copy_update(data):
    x, y = data
    temp = x.copy()
    temp.update(y)
    return temp


def add_items(data):
    x, y = data
    return dict(list(x.items()) + list(y.items()))


def curly_star(data):
    x, y = data
    return {**x, **y}


def chain_map(data):
    x, y = data
    return dict(ChainMap({}, y, x))


def itertools_chain(data):
    x, y = data
    return dict(chain(x.items(), y.items()))


def python39_concat(data):
    x, y = data
    return x | y


b = perfplot.bench(
    setup=setup,
    kernels=[
        copy_update,
        add_items,
        curly_star,
        chain_map,
        itertools_chain,
        python39_concat,
    ],
    labels=[
        "copy_update",
        "dict(list(x.items()) + list(y.items()))",
        "{**x, **y}",
        "chain_map",
        "itertools.chain",
        "x | y",
    ],
    n_range=[2 ** k for k in range(18)],
    xlabel="len(x), len(y)",
    equality_check=None,
)
b.save("out.png")
b.show()
于 2020-07-09T17:35:03.087 回答
25

尽管这个字典的答案很好,但这里定义的方法都没有真正进行深度字典合并。

示例如下:

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

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

于 2012-08-03T23:36:50.823 回答
23

如果你不介意变异x

x.update(y) or x

简单、易读、高性能。你知道 update()总是返回None,这是一个错误的值。x所以上面的表达式在更新后总是会计算为。

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

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

如果您还没有x变量,您可以lambda在不使用赋值语句的情况下使用来创建本地变量。这相当于使用lambdalet表达式,这是函数式语言中的一种常见技术,但可能是非 Python 的。

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

虽然它与以下使用 new 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是 3.9+ 上最 Pythonic 的。如果您必须支持旧版本,PEP 448样式{**x, **y}对于 3.5+ 是最简单的。但是,如果在您的(甚至更旧的)Python 版本中不可用,那么let 表达式模式也可以在这里使用。

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

(当然,这几乎等同于(z := x.copy()).update(y) or z,但如果您的 Python 版本足够新,那么 PEP 448 样式将可用。)

于 2017-09-22T02:57:15.753 回答
20

Python 3.9 中的功能:使用联合运算符 (|) 来合并dicts,类似于sets:

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

对于匹配的键,rightdict优先

这也适用于就地|=修改:dict

>>> e |= d    # e = e | d
>>> e
{'a': 1, 'c': 3, 'b': 2}
于 2020-06-01T21:23:12.493 回答
18

太傻了,.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
于 2014-03-02T01:44:39.697 回答
18

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

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

from functools import reduce

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

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

于 2016-03-28T13:13:27.860 回答
17

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

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。

于 2013-07-19T05:49:19.577 回答
17

迄今为止列出的解决方案的问题是,在合并字典中,键“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}
于 2013-12-03T18:11:54.923 回答
17

由于PEP 572: 赋值表达式, Python 3.8 版本(计划于 2019 年 10 月 20 日)发布时将有一个新选项。新的赋值表达式运算符允许您分配 的结果并仍然使用它来调用,使组合代码成为一个表达式,而不是两个语句,改变::=copyupdate

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

到:

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

同时在各方面表现相同。如果您还必须返回结果dict(您要求一个返回dict; 的表达式,上面创建并分配给newdict,但不返回它,因此您不能使用它按原样将参数传递给函数,a la myfunc((newdict := dict1.copy()).update(dict2))) ,然后只需添加or newdict到末尾(因为update返回None,这是虚假的,它会评估并返回newdict作为表达式的结果):

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

重要警告:总的来说,我不鼓励这种方法,而是支持:

newdict = {**dict1, **dict2}

解包方法更清晰(对于首先知道广义解包的任何人,您应该这样做),根本不需要结果的名称(因此在构造一个立即传递给函数或包含在list/tuple文字等中),并且几乎可以肯定也更快,(在 CPython 上)大致相当于:

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

但在 C 层使用具体dictAPI 完成,因此不涉及动态方法查找/绑定或函数调用调度开销(其中(newdict := dict1.copy()).update(dict2)不可避免地与原始的两行在行为上相同,以离散步骤执行工作,使用动态查找/绑定/调用方法。

它也更具可扩展性,因为合并三个dicts 很明显:

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

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

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

或者没有 s 的临时元组None,但对每个None结果进行真实性测试:

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

其中任何一个显然都更丑陋,并且包括进一步的低效率(要么浪费 s 用于逗号分隔的临时性tuple,要么对每个s 的返回进行None无意义的真实性测试以进行分隔)。updateNoneor

赋值表达式方法的唯一真正优势出现在以下情况:

  1. 您有需要处理sets 和dicts 的通用代码(它们都支持copyand update,因此代码的工作方式大致如您所愿)
  2. 您期望接收任意 dict-like objects,而不仅仅是dict它本身,并且必须保留左侧的类型和语义(而不是以 plain 结尾dict)。虽然myspecialdict({**speciala, **specialb})可能有效,但它会涉及额外的临时性dict,并且如果myspecialdict具有普通dict无法保留的功能(例如,常规dicts 现在基于键的第一次出现保留顺序,而值基于键的最后一次出现;您可能想要一个根据最后一个保留顺序的键的出现,因此更新值也会将其移至末尾),那么语义将是错误的。由于赋值表达式版本使用命名方法(它们可能被重载以适当地表现),它根本不会创建 a dict(除非dict1已经是 a dict),保留原始类型(和原始类型的语义),同时避免任何临时性。
于 2019-02-28T17:16:22.700 回答
16
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

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

于 2015-11-30T13:04:00.003 回答
13

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

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

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

于 2015-07-17T14:47:23.150 回答
9
>>> 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}
于 2013-11-13T10:01:31.680 回答
8

我知道这并不真正符合问题的具体情况(“一个班轮”),但是由于上面的答案都没有进入这个方向,而很多答案都解决了性能问题,我觉得我应该贡献我的想法。

根据用例,可能不需要创建给定输入字典的“真实”合并字典。在许多情况下,执行此操作的视图可能就足够了,即合并字典一样工作的对象不会完全计算它。可以说是合并字典的懒惰版本。

在 Python 中,这相当简单,可以使用我帖子末尾显示的代码来完成。鉴于此,原始问题的答案将是:

z = MergeDict(x, y)

当使用这个新对象时,它的行为就像一个合并的字典,但是它将具有恒定的创建时间和恒定的内存占用,同时保持原始字典不变。创建它比建议的其他解决方案便宜得多。

当然,如果您经常使用结果,那么您将在某个时候达到创建真正合并字典的速度的极限。正如我所说,这取决于您的用例。

如果您曾经觉得自己更喜欢真正的合并dict,那么调用dict(z)会产生它(但当然比其他解决方案成本更高,所以这值得一提)。

你也可以使用这个类来制作一种写时复制字典:

a = { 'x': 3, 'y': 4 }
b = MergeDict(a)  # we merge just one dict
b['x'] = 5
print b  # will print {'x': 5, 'y': 4}
print a  # will print {'y': 4, 'x': 3}

这是直接的代码MergeDict

class MergeDict(object):
  def __init__(self, *originals):
    self.originals = ({},) + originals[::-1]  # reversed

  def __getitem__(self, key):
    for original in self.originals:
      try:
        return original[key]
      except KeyError:
        pass
    raise KeyError(key)

  def __setitem__(self, key, value):
    self.originals[0][key] = value

  def __iter__(self):
    return iter(self.keys())

  def __repr__(self):
    return '%s(%s)' % (
      self.__class__.__name__,
      ', '.join(repr(original)
          for original in reversed(self.originals)))

  def __str__(self):
    return '{%s}' % ', '.join(
        '%r: %r' % i for i in self.iteritems())

  def iteritems(self):
    found = set()
    for original in self.originals:
      for k, v in original.iteritems():
        if k not in found:
          yield k, v
          found.add(k)

  def items(self):
    return list(self.iteritems())

  def keys(self):
    return list(k for k, _ in self.iteritems())

  def values(self):
    return list(v for _, v in self.iteritems())
于 2016-05-18T15:57:32.933 回答
8

仅限 Python 3.9+

合并 (|) 和更新 (|=) 运算符已添加到内置dict类中。

>>> d = {'spam': 1, 'eggs': 2, 'cheese': 3}
>>> e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
>>> d | e
{'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

增强的分配版本就地运行:

>>> d |= e
>>> d
{'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

PEP 584

于 2020-04-09T08:20:06.570 回答
8

短而精巧的方式

>>> z = {**x, **y}
>>> print(z)
{'a': 1, 'b': 10, 'c': 11}
于 2022-01-10T03:36:45.687 回答
7

在 Python 3.9 中

基于PEP 584,新版本的 Python 引入了两个新的字典运算符:联合 (|) 和就地联合 (|=)。您可以使用 | 合并两个字典,而 |= 将更新一个字典:

>>> pycon = {2016: "Portland", 2018: "Cleveland"}
>>> europython = {2017: "Rimini", 2018: "Edinburgh", 2019: "Basel"}

>>> pycon | europython
{2016: 'Portland', 2018: 'Edinburgh', 2017: 'Rimini', 2019: 'Basel'}

>>> pycon |= europython
>>> pycon
{2016: 'Portland', 2018: 'Edinburgh', 2017: 'Rimini', 2019: 'Basel'}

如果 d1 和 d2 是两个字典,则d1 | d2{**d1, **d2}. 该| 运算符用于计算集合的并集,因此您可能已经熟悉该符号。

使用的一个优点|是它适用于不同的类字典类型并通过合并保持类型:

>>> from collections import defaultdict
>>> europe = defaultdict(lambda: "", {"Norway": "Oslo", "Spain": "Madrid"})
>>> africa = defaultdict(lambda: "", {"Egypt": "Cairo", "Zimbabwe": "Harare"})

>>> europe | africa
defaultdict(<function <lambda> at 0x7f0cb42a6700>,
  {'Norway': 'Oslo', 'Spain': 'Madrid', 'Egypt': 'Cairo', 'Zimbabwe': 'Harare'})

>>> {**europe, **africa}
{'Norway': 'Oslo', 'Spain': 'Madrid', 'Egypt': 'Cairo', 'Zimbabwe': 'Harare'}

当您想有效地处理丢失的键时,您可以使用 defaultdict。请注意,|保留默认字典,而{**europe, **africa}不会。

|字典的+工作方式和列表的工作方式之间有一些相似之处。实际上,+运算符最初也被提议用于合并字典。当您查看就地运算符时,这种对应关系变得更加明显。

的基本用途|=是就地更新字典,类似于.update()

>>> libraries = {
...     "collections": "Container datatypes",
...     "math": "Mathematical functions",
... }
>>> libraries |= {"zoneinfo": "IANA time zone support"}
>>> libraries
{'collections': 'Container datatypes', 'math': 'Mathematical functions',
 'zoneinfo': 'IANA time zone support'}

当您将字典与 合并时|,两个字典都需要是正确的字典类型。另一方面,就地运算符 ( |=) 很乐意使用任何类似字典的数据结构:

>>> libraries |= [("graphlib", "Functionality for graph-like structures")]
>>> libraries
{'collections': 'Container datatypes', 'math': 'Mathematical functions',
 'zoneinfo': 'IANA time zone support',
 'graphlib': 'Functionality for graph-like structures'}
于 2020-10-06T15:23:53.467 回答
5

使用 dict 理解,您可以

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() }
于 2013-05-27T09:04:20.627 回答
5

我很好奇我是否可以通过单行字符串化方法来击败接受的答案的时间:

我尝试了 5 种方法,之前没有提到过 - 全部是一个班轮 - 都产生了正确的答案 - 我无法接近。

所以......为了省去你的麻烦,也许还能满足你的好奇心:

import json
import yaml
import time
from ast import literal_eval as literal

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

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

start = time.time()
for i in range(10000):
    z = yaml.load((str(x)+str(y)).replace('}{',', '))
elapsed = (time.time()-start)
print (elapsed, z, 'stringify yaml')

start = time.time()
for i in range(10000):
    z = literal((str(x)+str(y)).replace('}{',', '))
elapsed = (time.time()-start)
print (elapsed, z, 'stringify literal')

start = time.time()
for i in range(10000):
    z = eval((str(x)+str(y)).replace('}{',', '))
elapsed = (time.time()-start)
print (elapsed, z, 'stringify eval')

start = time.time()
for i in range(10000):
    z = {k:int(v) for k,v in (dict(zip(
            ((str(x)+str(y))
            .replace('}',' ')
            .replace('{',' ')
            .replace(':',' ')
            .replace(',',' ')
            .replace("'",'')
            .strip()
            .split('  '))[::2], 
            ((str(x)+str(y))
            .replace('}',' ')
            .replace('{',' ').replace(':',' ')
            .replace(',',' ')
            .replace("'",'')
            .strip()
            .split('  '))[1::2]
             ))).items()}
elapsed = (time.time()-start)
print (elapsed, z, 'stringify replace')

start = time.time()
for i in range(10000):
    z = json.loads(str((str(x)+str(y)).replace('}{',', ').replace("'",'"')))
elapsed = (time.time()-start)
print (elapsed, z, 'stringify json')

start = time.time()
for i in range(10000):
    z = merge_two_dicts(x, y)
elapsed = (time.time()-start)
print (elapsed, z, 'accepted')

结果:

7.693928956985474 {'c': 11, 'b': 10, 'a': 1} stringify yaml
0.29134678840637207 {'c': 11, 'b': 10, 'a': 1} stringify literal
0.2208399772644043 {'c': 11, 'b': 10, 'a': 1} stringify eval
0.1106564998626709 {'c': 11, 'b': 10, 'a': 1} stringify replace
0.07989692687988281 {'c': 11, 'b': 10, 'a': 1} stringify json
0.005082368850708008 {'c': 11, 'b': 10, 'a': 1} accepted

我从中学到的是 JSON 方法是从字典字符串返回字典的最快方法(在那些尝试中);比我认为使用ast. 我还了解到,应该不惜一切代价避免使用 YAML 方法。

是的,我知道这不是最好/正确的方法。我很好奇它是否更快,但不是;我发帖证明了这一点。

于 2018-03-22T04:08:47.477 回答
5

对于 Python 3:

from collections import ChainMap
a = {"a":1, "b":2}
b = {"c":5, "d":8}
dict(ChainMap(a, b))  # {"a":1, "b":2, "c":5, "d":8}

如果您在两个字典中有相同的键,ChainMap将使用第一个键的值并忽略第二个键的值。干杯!

于 2019-05-31T17:10:27.723 回答
4

OP 的两个字典的联合将类似于:

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

具体来说,两个实体(xy)的并集包含x和/或的所有元素y。不幸的是,尽管帖子的标题,OP 要求的不是工会。

我下面的代码既不优雅也不单行,但我相信它与 union 的含义是一致的。

从OP的例子:

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

z = {}
for k, v in x.items():
    if not k in z:
        z[k] = [(v)]
    else:
        z[k].append((v))
for k, v in y.items():
    if not k in z:
        z[k] = [(v)]
    else:
        z[k].append((v))

{'a': [1], 'b': [2, 10], 'c': [11]}

是否可以更改想要的列表,但是如果字典包含列表(和嵌套列表)作为任一字典中的值,则上述方法将起作用。

于 2014-09-30T02:36:19.257 回答
4

你可以用toolz.merge([x, y])这个。

于 2016-11-18T12:53:29.477 回答
4

这是 Python 3.5 或更高版本的表达式,它使用以下方式合并字典reduce

>>> from functools import reduce
>>> l = [{'a': 1}, {'b': 2}, {'a': 100, 'c': 3}]
>>> reduce(lambda x, y: {**x, **y}, l, {})
{'a': 100, 'b': 2, 'c': 3}

注意:即使字典列表为空或仅包含一个元素,这也有效。

于 2018-04-15T23:02:55.577 回答
4

您可以使用字典理解,因此您将拥有一个可以解决您的问题并且独立于您使用的 python 版本的衬垫。

newdict = {k:v for somedict in (x,y) for k, v in somedict.items()}

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> newdict = {k:v for somedict in (x,y) for k, v in somedict.items()}
>>> print(newdict)
{'a': 1, 'b': 10, 'c': 11}

分解:

  • 第一个循环:k,v将被插入新字典somedict中,它们将从xy字典中获取所有键和值
  • 第二个循环:k,v将遍历 somedict 中的所有键和值并将它们存储在新创建的newdict 中
于 2021-09-30T10:17:06.457 回答
4

合并两个字典的最简单方法。

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

首先从键和值开始,dict1然后newDict用键和值修改dict2

于 2021-11-07T08:33:14.050 回答
4

一个简单的解决方案:

a={"name":"abc"}
b={"age":12}

解决方案:

z=a|b

输出 {'name': 'abc', 'age': 12}

于 2022-02-14T09:48:18.787 回答
2

我有一个未在此处指定的解决方案

z = {}
z.update(x) or z.update(y)

这不会像 y 一样更新 x。表现?我不认为它会非常慢。

于 2013-12-05T08:02:05.070 回答
2

我认为我丑陋的单线在这里只是必要的。

z = next(z.update(y) or z for z in [x.copy()])
# or
z = (lambda z: z.update(y) or z)(x.copy())
  1. 字典被合并。
  2. 单一的表达。
  3. 永远不敢使用它。

PS这是一个适用于两个版本的 Python 的解决方案。我知道 Python 3 有这个{**x, **y}东西,它是正确的使用方法(如果你仍然有 Python 2,那么迁移到 Python 3 是正确的做法)。

于 2018-05-11T10:00:39.717 回答
2

适用于 2.5+ 的 hacky 单线:

>>> a = dict(x=2, y=3)
>>> b = dict(y=4, z=5)
>>> c = 'No Effect' if a.update(b) else a
>>> c
{'x': 2, 'y': 4, 'z': 5}

要记住的事情:

  • dict.updatedict就地修改,因此它评估为None
  • 在表达式A if C else B中,C首先计算。看这里

所以在这里,a.update(b)首先计算,a更新b和运算结果None,因此表达式将始终返回else条件中给定的值,即a。由于,a已经被修改,它将返回 的新值a,即更新后的 dict。

改进

这可以进一步改进,并且可以用于更旧的版本(可能也是 python 1.0?):

>>> c = a.update(b) or a

这里也是,第一部分产生None,因此它总是返回第二部分,但是由于更新操作已经完成,它总是返回更新的字典。

批判

  • 两种解决方案都会修改 的值a,因此如果想要保持两个输入字典不变,这不是一个好主意。

改进

如果需要副本,a可以稍微修改第二个:

>>> a = dict(x=2, y=3)
>>> b = dict(y=4, z=5)
>>> a, c = a.copy(), a.update(b) or a
>>> c
{'x': 2, 'y': 4, 'z': 5}
>>> d = dict(m=10, n=11)
>>> a, c = a.copy(), a.update(b) or a.update(d) or a
>>> c
{'x': 2, 'y': 4, 'z': 5, 'm': 10, 'n': 11}
>>> a
{'x': 2, 'y': 4}

注意事项

  • 对于任何数量大于 2 的字典,它(尤其是第一个)变得丑陋且不切实际
  • 此外,这不是显式的,它是un-pythonic

尽管这些解决方案非常快,尤其or是可能比新的 python 3.9 union 运算符更快的方法(不完全确定,需要进一步测试,如果有人想在之后添加它,欢迎他们),我不推荐这些方法由于上述原因。为了完整起见,添加了它。

于 2021-01-22T10:53:37.107 回答
2

从 Python 3.9+ 开始,有一种更简单的方法可以合并两个字典

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> merged = {**x, **y}
于 2021-06-29T21:26:52.837 回答
1

这个问题被标记python-3x了,但考虑到它是一个相对较新的添加,并且投票最多、接受的答案广泛涉及 Python 2.x 解决方案,我敢添加一个利用 Python 2.x 令人讨厌的特性的衬线列表理解,即名称泄漏...

$ python2
Python 2.7.13 (default, Jan 19 2017, 14:48:08) 
[GCC 6.3.0 20170118] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> [z.update(d) for z in [{}] for d in (x, y)]
[None, None]
>>> z
{'a': 1, 'c': 11, 'b': 10}
>>> ...

我很高兴地说,以上内容在任何版本的 Python 3 上都不再适用。

于 2017-05-30T12:28:26.923 回答
1

一些无需使用任何 Python 模块(无依赖关系)且只需几行代码即可解决此问题的方法。

所有 Python 版本(使用 Lambda):

merge_dicts = lambda old, new: old.update(new) or old

Python 版本 >= 3.5:

def merge_dicts(old, new):
    return {**old, **new} 

较旧的 Python 版本:

def merge_dicts(old, new):
    merged = old.copy()
    merged.update(new)
    return merged

此示例将合并新旧值,同时用新值擦除旧值。

用法:

old = {'name': 'Kevin', 'phone_number': '+33 12 34 45 67'}
new = {'name': 'Kevin', 'phone_number': '+33 88 88 88 88'}

print(merge_dicts(old, new))

输出:

{'name': 'Kevin', 'phone_number': '+33 88 88 88 88'}

如果您必须处理从旧版本合并到新版本的倍数,而不会丢失任何数据,则下面使用一组字典的示例方法:

所有 Python 版本:

def merge_dicts(old, news):
    merged = old.copy()
    for new in news:
        merged.update(new)
    return merged

用法:

old = {'name': 'Kevin', 'phone_number': '+33 12 34 45 67'}
new_01 = {'name': 'Kevin', 'phone_number': '+33 77 77 77 77', 'age': 28}
new_02 = {'name': 'SabK', 'phone_number': '+33 88 88 88 89'}
new_03 = {'phone_number': '+33 99 99 99 99'}

print(merge_dicts(old, [new_01, new_02, new_03]))

输出:

{'phone_number': '+33 99 99 99 99', 'age': 28, 'name': 'SabK'}

在此示例中,将从旧字典(第一个参数)生成新字典,然后从数组的第一个元素顺序更新到最后一个元素(new_01 > new_02 > new_03)

最后,您将从所有字典中获取所有数据,并将更新已更改的值。当您处理频繁更改的数据时,此功能非常有用。

于 2019-12-06T22:46:32.030 回答
1

dicts的深度合并:

from typing import List, Dict
from copy import deepcopy

def merge_dicts(*from_dicts: List[Dict], no_copy: bool=False) -> Dict :
    """ no recursion deep merge of 2 dicts

    By default creates fresh Dict and merges all to it.

    no_copy = True, will merge all dicts to a fist one in a list without copy.
    Why? Sometime I need to combine one dictionary from "layers".
    The "layers" are not in use and dropped immediately after merging.
    """
    
    if no_copy:
        xerox = lambda x:x
    else:
        xerox = deepcopy
    
    result = xerox(from_dicts[0])

    for _from in from_dicts[1:]:
        merge_queue = [(result, _from)]
        for _to, _from in merge_queue:
            for k, v in _from.items():
                if k in _to and isinstance(_to[k], dict) and isinstance(v, dict):
                    # key collision add both are dicts.
                    # add to merging queue
                    merge_queue.append((_to[k], v))
                    continue
                _to[k] = xerox(v)

    return result

用法:

print("=============================")
print("merge all dicts to first one without copy.")
a0 = {"a":{"b":1}}
a1 = {"a":{"c":{"d":4}}}
a2 = {"a":{"c":{"f":5}, "d": 6}}
print(f"a0 id[{id(a0)}] value:{a0}")
print(f"a1 id[{id(a1)}] value:{a1}")
print(f"a2 id[{id(a2)}] value:{a2}")
r = merge_dicts(a0, a1, a2, no_copy=True)
print(f"r  id[{id(r)}] value:{r}")

print("=============================")
print("create fresh copy of all")
a0 = {"a":{"b":1}}
a1 = {"a":{"c":{"d":4}}}
a2 = {"a":{"c":{"f":5}, "d": 6}}
print(f"a0 id[{id(a0)}] value:{a0}")
print(f"a1 id[{id(a1)}] value:{a1}")
print(f"a2 id[{id(a2)}] value:{a2}")
r = merge_dicts(a0, a1, a2)
print(f"r  id[{id(r)}] value:{r}")

于 2021-09-26T13:20:36.763 回答
0

从 Python 3.9PEP584 开始,有一种新方法可用:

z = x.union(y)

现在可以按您的意愿工作,无需修改 x 或 y。

y 值将使用相同的键覆盖 x 值。

您现在还可以为此使用联合合并语法

z = x | y

这给出了相同的结果。

于 2020-10-06T06:24:29.770 回答
0

相反,如果你想通过添加值来组合两个字典,我们可以依赖 Collections 模块(我不确定这是否存在于 12 年前 - 当第一次提出问题时):

from collections import Counter
x = Counter({'a': 1, 'b': 2})
y = Counter({'b': 10, 'c': 11})

那么x + y相当于

Counter({'a': 1, 'b': 12, 'c': 11})
于 2020-10-11T23:42:01.503 回答
0

我还没有看到任何深度合并的方法(不过我很乐意得到纠正)。利用 | 3.9+ 中的运算符用于 dictnew是一组默认设置的用例,而 dictexisting是一组正在使用的现有设置。new我的目标是在不覆盖现有设置的情况下合并任何添加的设置existing。我相信这种递归实现将允许一个人用另一个字典的新值升级一个字典。

欢迎发表评论,因为这是我对 SO 的第一个真正的小说贡献之一。如果代码不符合要求,我很乐意得到纠正。

def merge_dict_recursive(new: dict, existing: dict):
    merged = new | existing

    for k, v in merged.items():
        if isinstance(v, dict):
            if k not in existing:
                # key is not in existing dict at all, so add entire value
                existing[k] = new[k]

            merged[k] = merge_dict_recursive(new[k], existing[k])
    return merged

示例测试数据:

new
{'dashboard': True,
 'depth': {'a': 1, 'b': 22222, 'c': {'d': {'e': 69}}},
 'intro': 'this is the dashboard',
 'newkey': False,
 'show_closed_sessions': False,
 'version': None,
 'visible_sessions_limit': 9999}
existing
{'dashboard': True,
 'depth': {'a': 5},
 'intro': 'this is the dashboard',
 'newkey': True,
 'show_closed_sessions': False,
 'version': '2021-08-22 12:00:30.531038+00:00'}
merged
{'dashboard': True,
 'depth': {'a': 5, 'b': 22222, 'c': {'d': {'e': 69}}},
 'intro': 'this is the dashboard',
 'newkey': True,
 'show_closed_sessions': False,
 'version': '2021-08-22 12:00:30.531038+00:00',
 'visible_sessions_limit': 9999}
于 2021-08-22T16:07:45.127 回答
-1

你可以简单地使用python中的更新方法——x.update(y)。这将覆盖现有密钥。

于 2021-08-20T09:34:35.197 回答