Python中字典的合并层次结构

Python中字典的合并层次结构,python,dictionary,merge,Python,Dictionary,Merge,我有两本字典,我想做的有点奇怪。基本上,我想合并它们。这很简单。但它们是字典的层次结构,我想以这样的方式合并它们,如果字典中的一个项本身就是一个字典,并且两者都存在,我也想合并这些字典。如果不是字典,我希望第二个字典中的值覆盖第一个字典中的值。有点像这样: 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

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

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 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]}
(我在这里使用的是Python3,可以用Python2中的
.items
替换
.items

因为这有点冗长,所以总是有一种偷偷摸摸的方法来合并两条格言:

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

您可能希望也可能不希望使用此语法-虽然它很紧凑,但它在某种程度上依赖于实现细节。

假设您可能有嵌套字典(基于您对递归的思考),类似的东西应该可以工作

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]}}
合并(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)

需要类似的东西,并实现了更直接的递归解决方案。就地更新命令“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]}}

如果
1
的值是一个dict中的一个列表,而
2
是另一个dict中的一个dict呢?通常我认为这不会发生,但在这种情况下,我猜会用另一个dictionary中的值覆盖。呃,快速问题:在Python 2.6.2中尝试此操作时,我遇到语法错误。我忘了提到我需要在2.6.2和2.7.3中运行它。我猜这段代码中有一些东西是在2.6.2之后添加的?对,所以第一个“return”语句使用字典理解?我可以把它分解成一个循环,在2.6.2中应该可以正常工作。不那么优雅,但是嘿。@SimonLundberg您可以将dict理解分解成一个循环,或者您也可以对键、值元组的生成器表达式调用
dict
函数。对于2.6,字典的
viewkeys
方法也会有问题。2.6版本的解决方案请参见我的编辑。哦,那么当你在生成器上调用dict(或者,可能是现有的iterable?)时,它会生成字典吗?我错过了。很高兴知道。啊,谢谢你提醒我关于查看键的事。事实证明这不是问题,因为合并函数实际上只在2.7.3环境中被调用。基本上,我必须为不同版本的主机软件实现一些可选的代码路径,因为它们有一些不同。旧版本运行的是2.6.2,新版本是2.7.3。为了以防万一,我将从循环版本切换到dict生成器组合。谢谢