Python字典联合不能按预期工作,深度大于1的字典

时间:2017-09-22 02:13:36

标签: python dictionary

这是一个说明问题的例子......

a = {
    "foo" : 2,
    "bar" : 3,
}

b = {
    "bar" : 4,
    "zzz" : 5,
}

print(json.dumps(dict(a, **b), indent=4))

这会给你以下结果......

{
    "foo": 2,
    "bar": 4,
    "zzz": 5
}

请注意"foo"中的a键是如何添加到结果中的?

现在看看这个例子......

a = {
    "foo" : {
        "1" : {
            "foo" : True,
        },
        "2" : {
            "foo" : True,
        },
        "3" : {
            "foo" : True,
        },
        "4" : {
            "foo" : True
        }
    }
}

b = {
    "foo" : {
        "1" : {
            "foo" : True,
        },
        "2" : {
            "foo" : True,
        },
        "3" : {
            "foo" : False,
        }
    }
}

print(json.dumps(dict(a, **b), indent=4))

这会给你以下结果......

{
    "foo": {
        "1": {
            "foo": true
        },
        "3": {
            "foo": false
        },
        "2": {
            "foo": true
        }
    }
}

"3"已更新,就像上一个示例中"bar"的更新方式一样,但请注意"4"中的a键未添加到结果中,如何"foo" 1}}在上一个例子中?

所以我的预期输出是:

{
    "foo": {
        "1": {
            "foo": true
        },
        "3": {
            "foo": false
        },
        "2": {
            "foo": true
        },
        "4": {
            "foo": true
        }
    }
}

如何修改此词典联合进程,以便保留a中不在b中的键?

我的总体目标是让我的字典a,但b中的任何值都会被覆盖。

2 个答案:

答案 0 :(得分:2)

让我们来谈谈这种行为。我认为关键点是**kwargs的工作原理。

在第一种情况下,当您将**b传递给dict()时,它与dict(a, bar=4, zzz=5)相同。因此,在这种情况下,{zzz: 5}被添加到字典中,{bar: 4}已更新。

但在第二种情况下,当您将**b传递给dict()时,它与dict(a, foo={...})相同。由于foo参数会覆盖a中的相同键,因此结果与b完全相同。因此,这不是更新操作,而是覆盖操作。

再举一个例子:

a = {
    "foo" : {
        "1" : {
            "foo" : True,
        },
        "2" : {
            "foo" : True,
        },
        "3" : {
            "foo" : True,
        },
        "4" : {
            "foo" : True
        }
    }
}

b = {
    "foo" : {
        "1" : {
            "foo" : True,
        },
        "2" : {
            "foo" : True,
        },
        "3" : {
            "foo" : False,
        },
        "5" : {
            "foo" : False,
        }
    }
}

print(json.dumps(dict(a, **b), indent=4))

output:
{
    "foo": {
        "1": {
            "foo": true
        },
        "2": {
            "foo": true
        },
        "3": {
            "foo": false
        },
        "5": {
            "foo": false
        }
    }
}

答案 1 :(得分:1)

对于您的特定情况,只有dict深度为1,您可以使用循环并调用dict.update()来更新值。您无法工作的原因是dict(a, **b)会覆盖共有的所有重复密钥ab,并且仅保留b&#39 ; s,表示a "foo"完全被b' s "foo"

取代
import json

for k in a.keys():
    try:
        a[k].update(b[k])
    except KeyError:
        continue

print(json.dumps(a, indent=4))

这将从"1" ,"2" ,"3" dictb "4" dict保持更新所需的结果a。但是如果你有一个n深度的词典,那将无法正常工作。您将需要使用递归函数,如下所示:

示例数据:

a = {
    "foo" : {
        "1" : {
            "foo" : {"1": True,
                     "2": False},
        },
        "2" : {
            "foo" : True,
        },
        "3" : {
            "foo" : True,
        },
        "4" : {
            "foo" : True
        }
    },
}

b = {
    "foo" : {
        "1" : {
            "foo" : {"1": True,
                     "3": True},
        },
        "2" : {
            "foo" : True,
        },
        "3" : {
            "foo" : False,
        }
    }
}

递归代码:

import json

def update(dctA, dctB):
    if isinstance(dctA, dict) and isinstance(dctB, dict):
        for k in set(dctA.keys()) & set(dctB.keys()):
            update(dctA[k], dctB[k])
            try:
                dctA[k].update(dctB[k])
                dctB.pop(k)
            except (KeyError, AttributeError):
                continue
    return dctA

print(json.dumps(update(a, b), indent=4))

输出将是:

{
    "foo": {
        "1": {
            "foo": {
                "1": true,
                "2": false,
                "3": true
            }
        },
        "2": {
            "foo": true
        },
        "3": {
            "foo": false
        },
        "4": {
            "foo": true
        }
    }
}

您可以看到,"3"密钥已更新,"foo"更改为false"4"保持为dict {{1} }没有更新密钥b。此外,由于递归函数,嵌套的"4"键也已更新,"foo" {"1": True, "2": False}已更新为{"1": True, "3": True}{"1": true, "2": false, "3": true}保持不变,"1"被保留,因为"2" dict没有密钥b,密钥"2"已创建,因为它是目前在"3" dict