在Python中合并字典的层次结构

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Python中合并字典的层次结构相关的知识,希望对你有一定的参考价值。

我有两本词典,我想做的事情有点奇怪。基本上,我想合并它们。这很简单。但它们是字典的层次结构,我希望以这样的方式合并它们:如果字典中的项本身就是字典并且存在于两者中,我也想合并这些字典。如果它不是字典,我希望第二个字典中的值覆盖第一个字典中的值。有点像这样:

a = {0: {0: "a"},
     1: [0, 1, 2]}

b = {0: {1: "b"},
     1: [3, 4, 5]}

Merge(a, b)

#output:
{0: {0: "a",
     1: "b"},
 1: [3, 4, 5]}

那有意义吗?因为键“0”在a和b中都包含字典,所以它也合并了这些字典。但是在第二个键的情况下,它是一个列表,所以它只是覆盖了它。

所以我想我会看一些递归函数?不太确定如何处理这个问题。

谢谢!

编辑:我忘了提一个非常重要的细节:

我需要一个适用于2.6.2和2.7.3的功能。

答案

假设您可能有嵌套字典(基于您在递归方面的想法),这样的事情应该有效,

from copy import deepcopy

def merge(a, b):
    if isinstance(b, dict) and isinstance(a, dict):
        a_and_b = a.viewkeys() & b.viewkeys()
        every_key = a.viewkeys() | b.viewkeys()
        return {k: merge(a[k], b[k]) if k in a_and_b else 
                   deepcopy(a[k] if k in a else b[k]) for k in every_key}
    return deepcopy(b)

merge(a, b)的返回值在概念上就像创建a的(深层)副本并运行a.update(b)的递归版本。


使用一些嵌套示例,

a = {0: {0: 'a'},
     1: [0, 1, 2],
     2: [9, 9],
     3: {'a': {1: 1, 2: 2}, 'b': [0, 1]}}

b = {0: {1: 'b'},
     1: [3, 4, 5],
     2: {22: 22, 33: 33},
     3: {'a': {2: 22, 3: 33}, 'b': [99, 88]}}

merge(a, b)生产,

{0: {0: 'a', 1: 'b'},
 1: [3, 4, 5],
 2: {22: 22, 33: 33},
 3: {'a': {1: 1, 2: 22, 3: 33}, 'b': [99, 88]}}

编辑:Python 2.6版本

def merge(a, b):
    if isinstance(b, dict) and isinstance(a, dict):
        a_and_b = set(a).intersection(b)
        every_key = set(a).union(b)
        return dict((k, merge(a[k], b[k]) if k in a_and_b else
                     deepcopy(a[k] if k in a else b[k])) for k in every_key)
    return deepcopy(b)
另一答案

嗯..只要你不是任意嵌套,你就不需要递归。

from itertools import chain

{k:(v if not isinstance(v,dict) else dict(chain(a[k].items(), v.items()))) for k,v in b.items()}
Out[10]: {0: {0: 'a', 1: 'b'}, 1: [3, 4, 5]}

(我在这里使用python 3,随意在python 2中用.items替换.iteritems

由于这有点冗长,因此总是采用偷偷摸摸的方式合并两个dicts:

{k:(v if not isinstance(v,dict) else dict(a[k], **v)) for k,v in b.items()}
Out[11]: {0: {0: 'a', 1: 'b'}, 1: [3, 4, 5]}

您可能想也可能不想使用这种语法 - 虽然它是紧凑的,但它有点滥用cPython实现细节。

另一答案

需要类似的东西,并实现了一个更直接的递归解决方案。就地更新dict'd'。

from Collections import MutableMapping

def merge(d, v):
    """
    Merge two dictionaries.

    Merge dict-like `v` into dict-like `d`. In case keys between them are the same, merge
    their sub-dictionaries where possible. Otherwise, values in `v` overwrite `d`.
    """
    for key in v:
        if key in d and isinstance(d[key], MutableMapping) and isinstance(v[key], MutableMapping):
            d[key] = merge(d[key], v[key])
        else:
            d[key] = v[key]
    return d

例1:

a = {0: {0: "a"},
     1: [0, 1, 2]}

b = {0: {1: "b"},
     1: [3, 4, 5]}

>>> merge(a, b)
{0: {0: 'a', 1: 'b'}, 1: [3, 4, 5]}

例2:

a = {0: {0: 'a'},
     1: [0, 1, 2],
     2: [9, 9],
     3: {'a': {1: 1, 2: 2}, 'b': [0, 1]}}

b = {0: {1: 'b'},
     1: [3, 4, 5],
     2: {22: 22, 33: 33},
     3: {'a': {2: 22, 3: 33}, 'b': [99, 88]}}

>>> merge(a, b) 
{0: {0: 'a', 1: 'b'},
 1: [3, 4, 5],
 2: {22: 22, 33: 33},
 3: {'a': {1: 1, 2: 22, 3: 33}, 'b': [99, 88]}}

以上是关于在Python中合并字典的层次结构的主要内容,如果未能解决你的问题,请参考以下文章

13 个非常有用的 Python 代码片段

通过合并空白主字典中的字典,在 Python 中创建具有特定结构的 JSON

当事先不知道层次结构中的键位置时,如何访问嵌套的 Python 字典键值对?

在 Android Navigation 组件中使用 backstack 打开不同层次结构中的片段

多片段层次结构中的菜单膨胀问题

Python snippet(代码片段)