例如,我有两个字典:
Dict A: {'a': 1, 'b': 2, 'c': 3}
Dict B: {'b': 3, 'c': 4, 'd': 5}
我需要一种python的方式来“组合”两个字典,这样的结果是:
{'a': 1, 'b': 5, 'c': 7, 'd': 5}
也就是说:如果一个键在两个字典中都出现,则将它们的值相加,如果它只在一个字典中出现,则保留其值。
例如,我有两个字典:
Dict A: {'a': 1, 'b': 2, 'c': 3}
Dict B: {'b': 3, 'c': 4, 'd': 5}
我需要一种python的方式来“组合”两个字典,这样的结果是:
{'a': 1, 'b': 5, 'c': 7, 'd': 5}
也就是说:如果一个键在两个字典中都出现,则将它们的值相加,如果它只在一个字典中出现,则保留其值。
当前回答
这是一个很一般的解。你可以处理任意数量的dict +键,只在一些dict +容易使用任何聚合函数你想要:
def aggregate_dicts(dicts, operation=sum):
"""Aggregate a sequence of dictionaries using `operation`."""
all_keys = set().union(*[el.keys() for el in dicts])
return {k: operation([dic.get(k, None) for dic in dicts]) for k in all_keys}
例子:
dicts_same_keys = [{'x': 0, 'y': 1}, {'x': 1, 'y': 2}, {'x': 2, 'y': 3}]
aggregate_dicts(dicts_same_keys, operation=sum)
#{'x': 3, 'y': 6}
不相同的键和泛型聚合示例:
dicts_diff_keys = [{'x': 0, 'y': 1}, {'x': 1, 'y': 2}, {'x': 2, 'y': 3, 'c': 4}]
def mean_no_none(l):
l_no_none = [el for el in l if el is not None]
return sum(l_no_none) / len(l_no_none)
aggregate_dicts(dicts_diff_keys, operation=mean_no_none)
# {'x': 1.0, 'c': 4.0, 'y': 2.0}
其他回答
人物介绍: 有(可能)最好的解决方案。但你必须知道并记住它,有时你必须希望你的Python版本不是太旧或其他问题。
还有一些最“俗气”的解决方案。它们伟大而简短,但有时却很难理解、阅读和记忆。
不过,还有另一种选择,那就是尝试重新发明轮子。 -为什么要重新发明轮子? -一般来说,这是一个很好的学习方法(有时只是因为现有的工具不能完全按照你想要的方式来做),如果你不知道或不记得解决你的问题的完美工具,这是最简单的方法。
因此,我建议从collections模块重新发明Counter类的轮子(至少部分地):
class MyDict(dict):
def __add__(self, oth):
r = self.copy()
try:
for key, val in oth.items():
if key in r:
r[key] += val # You can custom it here
else:
r[key] = val
except AttributeError: # In case oth isn't a dict
return NotImplemented # The convention when a case isn't handled
return r
a = MyDict({'a':1, 'b':2, 'c':3})
b = MyDict({'b':3, 'c':4, 'd':5})
print(a+b) # Output {'a':1, 'b': 5, 'c': 7, 'd': 5}
可能还有其他的方法来实现它,而且已经有工具可以做到这一点,但是把事情的基本原理可视化总是很好的。
这个解决方案很容易使用,它被用作一个普通的字典,但你可以使用求和函数。
class SumDict(dict):
def __add__(self, y):
return {x: self.get(x, 0) + y.get(x, 0) for x in set(self).union(y)}
A = SumDict({'a': 1, 'c': 2})
B = SumDict({'b': 3, 'c': 4}) # Also works: B = {'b': 3, 'c': 4}
print(A + B) # OUTPUT {'a': 1, 'b': 3, 'c': 6}
另外,请注意a.update(b)比a + b快2倍
from collections import Counter
a = Counter({'menu': 20, 'good': 15, 'happy': 10, 'bar': 5})
b = Counter({'menu': 1, 'good': 1, 'bar': 3})
%timeit a + b;
## 100000 loops, best of 3: 8.62 µs per loop
## The slowest run took 4.04 times longer than the fastest. This could mean that an intermediate result is being cached.
%timeit a.update(b)
## 100000 loops, best of 3: 4.51 µs per loop
下面是另一个使用字典推导式结合dict()行为的选项:
dict3 = dict(dict1, **{ k: v + dict1.get(k, 0) for k, v in dict2.items() })
# {'a': 4, 'b': 2, 'c': 7, 'g': 1}
从https://docs.python.org/3/library/stdtypes.html dict类型:
https://docs.python.org/3/library/stdtypes.html#dict
也
如果给出了关键字参数,则关键字参数及其值将被添加到由位置参数创建的字典中。
词典理解
**{ k: v + dict1.get(v, 0), v in dict2.items() }
处理将dict1[1]添加到v.中。这里不需要显式if,因为dict1的默认值。Get可以设置为0。
一个更通用的解决方案,它也适用于非数字值:
a = {'a': 'foo', 'b':'bar', 'c': 'baz'}
b = {'a': 'spam', 'c':'ham', 'x': 'blah'}
r = dict(a.items() + b.items() +
[(k, a[k] + b[k]) for k in set(b) & set(a)])
或者更普通一点:
def combine_dicts(a, b, op=operator.add):
return dict(a.items() + b.items() +
[(k, op(a[k], b[k])) for k in set(b) & set(a)])
例如:
>>> a = {'a': 2, 'b':3, 'c':4}
>>> b = {'a': 5, 'c':6, 'x':7}
>>> import operator
>>> print combine_dicts(a, b, operator.mul)
{'a': 10, 'x': 7, 'c': 24, 'b': 3}