合并字典中的字典并求和值

3
我正在寻找一种方法将多个包含嵌套字典的字典彼此合并。嵌套字典的数量不是固定的,而是动态的。
最终字典应包含所有嵌套字典和它们值的总和:
COUNTRY1 = {'a': {'X': 10, 'Y': 18, 'Z': 17}, 'b': {'AA':{'AAx':45,'AAy':22},'BB':{'BBx':45,'BBy':22}}, 'c': 100}
COUNTRY2 = {'a': {'U': 12, 'V': 34, 'W': 23}, 'b': {'AA':{'AAz':23,'AAa':26},'BB':{'BBz':11,'BBa':15}}, 'c': 115}
COUNTRY3 = {'a': {'Y': 15, 'Z': 14, 'X': 12}, 'b': {'AA':{'AAx':45,'AAz':22},'BB':{'BBy':45,'BBz':22}}, 'c': 232}

# After merging the dictionaries the result should look like:
ALL
>>> {'a': {'X': 22, 'Y': 33, 'Z': 31, 'U': 12, 'V': 34, 'W': 23}, 'b': {'AA':{'AAx':90,'AAy':22,'AAz':45,'AAa':26},'BB':{'BBx':45,'BBy':67, 'BBz':33,'BBa':15}}, 'c': 447}

我尝试了以下代码,它允许最多3个嵌套的字典。不幸的是,这段代码没有达到我期望的效果。我感觉这可以通过递归函数来完成,但我找不到方法。
COUNTRIES = ['COUNTRY1','COUNTRY2', 'COUNTRY3']
ALL = {}
for COUNTRY_CODE in COUNTRIES:

    COUNTRY = pickle.load(open(COUNTRY_CODE+".p", "rb"))
    keys = COUNTRY.keys()
    for key in keys:
        try:
            keys2 = COUNTRY[key].keys()
            print(key, keys2)

            for key2 in keys2:
                try:
                    keys3 = COUNTRY[key][key2].keys()
                    print(key2, keys3)

                    for key3 in keys3:
                        try:
                            keys4 = COUNTRY[key][key2][key3].keys()
                            print(key3, keys4)
                        except:
                            print(key3, "NO KEY3")
                            if not key3 in ALL[key][key2]:
                                ALL[key][key2][key3] = COUNTRY[key][key2][key3]
                            else:
                                ALL[key][key2][key3] =+ COUNTRY[key][key2][key3]

                except:
                    print(key2, "NO KEY2")
                    if not key2 in ALL[key]:
                        ALL[key][key2] = COUNTRY[key][key2]
                    else:
                        ALL[key][key2] =+ COUNTRY[key][key2]

        except:
            print(key, "NO KEY")
            if not key in ALL:
                ALL[key] = COUNTRY[key]
            else:
                ALL[key] =+ COUNTRY[key]

print(ALL)

你尝试了什么?请粘贴一些内容。 - hygull
我刚刚将我的代码添加到帖子中。 - Sem
1
你的代码在哪里出错了?你当前的输出是什么样子的? - Mike Tung
如果不同字典中的键是唯一的,那么代码似乎可以工作,但是如果键相同,则第二个字典将覆盖第一个字典的值。 - Sem
2个回答

4
问题在于您需要根据值的类型确定要对字典键执行什么操作。基本思路如下:
  • 输入是一对字典,输出是总和字典
  • 沿着两个输入字典进行步进
  • 如果一个值是一个字典,则递归
  • 如果一个值是一个数字,则将其添加到另一个数字中
使用推导式实现这个功能相当容易:
def add_dicts(d1, d2):
    def sum(v1, v2):
        if v2 is None:
            return v1
        try:
            return v1 + v2
        except TypeError:
            return add_dicts(v1, v2)
    result = d2.copy()
    result.update({k: sum(v, d2.get(k)) for k, v in d1.items()})
    return result

复制操作确保了任何在d2中而不在d1中的键都能被简单地复制过去。
现在可以按照以下方式进行求和:
ALL = add_dicts(add_dicts(COUNTRY1, COUNTRY2), COUNTRY3)

更普遍的情况是,您可以使用 functools.reduce 来对无限数量的字典进行操作:
dicts = [COUNTRY1, COUNTRY2, COUNTRY3]
ALL = reduce(add_dicts, dicts)

2
制作两个如下的函数:
def cal_sum(lst):
    final_dict = dict()
    for l in lst:
        sum(final_dict,l)
    return final_dict

def sum(final_dict,iter_dict):
    for k, v in iter_dict.items():
        if isinstance(v, dict):
            sum(final_dict.setdefault(k, dict()), v)
        elif isinstance(v, int):
            final_dict[k] = final_dict.get(k, 0) + v

调用上述代码如下所示,将产生所需的输出:
>>> print(cal_sum([COUNTRY1, COUNTRY2, COUNTRY3]))
{'a': {'U': 12, 'W': 23, 'V': 34, 'Y': 33, 'X': 22, 'Z': 31}, 'c': 447, 'b': {'AA': {'AAa': 26, 'AAy': 22, 'AAx': 90, 'AAz': 45}, 'BB': {'BBa': 15, 'BBz': 33, 'BBy': 67, 'BBx': 45}}}

另一个看起来很干净的答案,但是在Python 3.6中对我没有用。我认为在Python 3中不再允许使用iteritems()方法了? - Sem
@Sem 已更新答案。现在检查它是否有效? - Pankaj Singhal
由于某些原因,在运行此代码后,一些嵌套字典为空。我现在将坚持使用@Mad Physicist的代码,但是感谢你的努力! - Sem
对于您问题中的输入,它运行良好。您能否列出导致问题的字典? - Pankaj Singhal
我发现问题是因为我的字典中有些值是浮点数而不是整数。我将最后一个elif改为: elif isinstance(v, int) or isinstance(v, float): 这解决了问题。我喜欢这个解决方案,因为我可以针对每种类型指定如何处理数据,这可能比@Mad Physicist的解决方案更强大。 - Sem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接