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

  • 问题:
  • 我有两个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.更新()也是我要找的。)

  • 答案:
  • 对于字典xyz变成了一个简单的合并字典,其中来自y的值替换来自x的值

    在Python 3.5或更高版本中:

      z = {**x, **y}

    在Python2(或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)

    在Python 3.9.0a4或更高版本中(最终发布日期约为2020年10月):PEP-584的实现进一步简化了这一点:

      z = x | y          # NOTE: 3.9+ ONLY

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

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

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

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

    一种新的语法,在PEP 448z = {**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,现在它已进入Python3.5的新增功能文档

    但是,由于许多组织仍然使用Python2,所以您可能希望以向后兼容的方式来实现这一点。Python 2和Python 3.0-3.4中提供的经典Python方法是将其分为两个步骤:

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

    在这两种方法中,y将排在第二位,其值将取代x的值,因此在我们的最终结果中,'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 = 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

    这个函数在python2和python3中适用于所有字典。e、 g.给定字典ag

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

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

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

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

    在Python2中,为每个dict在内存中创建两个列表,在内存中创建一个长度等于前两个的长度的第三个列表,然后放弃所有三个列表来创建dict。在Python 3中,这将失败,因为要将两个dict项目添加到一起,而不是两个列表-

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

    类似地,在Python3中(Python2.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中的值被保留:

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

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

    z = dict(x, **y)

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

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

    字典打算使用散列键(例如frozensets或tuples),但是当键不是字符串时,这个方法在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写道:

    我很满意

    以及

    显然dict(x,**y)是“酷黑客”的代名词

    这是我的理解(以及creator of the language)dict(**y)的预期用途是为了便于阅读而创建词典,例如:

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

    而不是

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

    不管Guido怎么说,dict(x,**y)符合dict规范,顺便说一句,dict规范适用于python2和python3。事实上,这只适用于字符串键,这是关键字参数工作方式的直接结果,而不是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的其他实现(pypyy、Jython、IronPython),这种不一致性是很糟糕的。因此,它在python3中得到了修复,因为这种用法可能是一个突破性的变化

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

    更多评论:

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

    我的回答是:合并两个dicts(x,y)在我看来,如果我们真的关心可读性的话。而且它不具有向前兼容性,因为Python2越来越不受欢迎

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

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

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

    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”

    这些方法性能较差,但它们将提供正确的行为。

    也可以在dict comprehension公司名称:

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

    或者在Python2.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

    以下是在ubuntu14.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

    在Python3.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