我有两个 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 字典合并到一个表达式中?

对于词典 xy, z 成为一个浅层合并的字典,其值来自 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)
    

解释

假设你有两个字典,你想将它们合并到一个新的字典中,而不改变原始的字典:

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

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

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

为此提出了一种新语法 公众号 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的值,因此 '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)

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

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

此函数适用于 Python 2 和 3 中的所有字典。例如给定的字典 ag:

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

和键值对 g 将优先于字典 af, , 等等。

对其他答案的批评

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

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

在Python 2中,您在内存中为每个字典创建两个列表,在内存中创建第三个列表,其长度等于前两个列表加在一起的长度,然后丢弃所有三个列表以创建字典。 在 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 中(viewitems() 当值是不可散列的对象(例如列表)时,Python 2.7 中的方法也会失败。即使你的值是可散列的, 由于集合在语义上是无序的,因此行为的优先级是未定义的。所以不要这样做:

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

这是一个用法示例 在 django 中修复.

字典旨在采用可散列键(例如冻结集或元组),但是 当键不是字符串时,此方法在 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)作为“ cool hack''的“ call x.update(y)”和“返回x”。就我个人而言,我发现它比酷更卑鄙。

这是我的理解(也是大家的理解) 语言的创造者)的预期用途 dict(**y) 用于创建字典以提高可读性,例如:

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

代替

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

对评论的回应

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

同样,当键是非字符串时,它不适用于 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: {}}}

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

性能较差但正确的临时任务

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

您还可以在字典理解中手动链接字典:

{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

词典资源

其他提示

对于您的情况,您可以做的是:

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

如您所愿,这将把最终的字典放入 z, ,并设置 key 的值 b 被第二个(y) 字典的值:

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

如果你使用Python 3,只是稍微复杂一点。创造 z:

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

替代:

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

另一个更简洁的选项:

z = dict(x, **y)

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

这可能不会是一个受欢迎的答案,但您几乎肯定不想这样做。如果您想要一个合并副本,请使用 copy (或 深复制, ,取决于您想要的),然后更新。这两行代码比使用 .items() + .items() 创建的单行代码更具可读性 - 更具 Python 风格。显式的比隐式的好。

此外,当您使用 .items() (Python 3.0 之前)时,您正在创建一个包含字典中的项目的新列表。如果你的字典很大,那么这是相当大的开销(两个大列表将在创建合并字典后立即被丢弃)。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() 可以在旧版本中使用。

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

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

让我得出这样的结论 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)

正如托尼所做的那样,但(毫不奇怪)符号的差异对性能没有任何可测量的影响。使用您认为合适的任何一个。当然,他指出两个语句的版本更容易理解,这是绝对正确的。

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

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

在Python 3中,你可以使用 集合.ChainMap 它将多个字典或其他映射组合在一起以创建单个可更新的视图:

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

递归/深度更新字典

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

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

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

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

就我个人而言,我最喜欢这个版本,因为它在单一函数语法中很好地描述了我想要的内容。唯一的小问题是,y 中的值优先于 x 中的值并不完全明显,但我认为弄清楚这一点并不困难。

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

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

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}

尽管问题已经多次回答,但尚未列出该问题的简单解决方案。

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

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

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

new = dict_merge(old, extras)

在这些阴暗和可疑的答案中,这个光辉的例子是在Python中合并字典的唯一好方法,受到独裁者的终身认可 吉多·范罗苏姆 他自己!其他人建议了其中一半,但没有将其放入函数中。

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

给出:

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

如果您认为 lambda 是邪恶的,那么就不要继续阅读了。根据要求,您可以使用一个表达式编写快速且节省内存的解决方案:

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}

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

变得Python化。用一个 理解:

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

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

在python3中, items 方法 不再返回列表, ,而是一个 看法, ,它的作用就像一个集合。在这种情况下,您需要采用集合并,因为与 + 不会工作:

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 回答 非常优雅,因为它可以采用任意数量的字典作为参数,但是 从文档 看起来它会依次查看每次查找的所有字典的列表:

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

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

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

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

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

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

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}

两本词典

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/

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

示例如下:

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

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

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

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

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

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

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

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

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

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

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

真是太傻了 .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

使用字典理解,你可以

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

我知道这并不真正符合问题的具体情况(“一行”),但因为 没有任何 上面的答案都朝着这个方向发展,而很多很多答案都解决了性能问题,我觉得我应该贡献我的想法。

根据用例,可能不需要创建给定输入字典的“真实”合并字典。A 看法 在许多情况下,这可能就足够了,i。e.一个行动的物体 喜欢 合并的字典不会完全计算它。可以说,是合并字典的惰性版本。

在 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())
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top