有没有任何python的方法来组合两个字典(为出现在两个字典中的键添加值)?

例如,我有两个字典:

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}

也就是说:如果一个键在两个字典中都出现,则将它们的值相加,如果它只在一个字典中出现,则保留其值。

166926 次浏览

使用# EYZ1:

>>> from collections import Counter
>>> A = Counter({'a':1, 'b':2, 'c':3})
>>> B = Counter({'b':3, 'c':4, 'd':5})
>>> A + B
Counter({'c': 7, 'b': 5, 'd': 5, 'a': 1})

计数器基本上是dict的一个子类,因此您仍然可以使用它们执行通常使用该类型执行的所有其他操作,例如遍历它们的键和值。

>>> A = {'a':1, 'b':2, 'c':3}
>>> B = {'b':3, 'c':4, 'd':5}
>>> c = {x: A.get(x, 0) + B.get(x, 0) for x in set(A).union(B)}
>>> print(c)


{'a': 1, 'c': 7, 'b': 5, 'd': 5}

一个更通用的解决方案,它也适用于非数字值:

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}

< >强介绍: 有(可能)最好的解决方案。但你必须知道并记住它,有时你必须希望你的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}

可能还有其他的方法来实现它,而且已经有工具可以做到这一点,但是把事情的基本原理可视化总是很好的。

没有额外进口!的那个

他们的神谕的标准被称为EAFP(请求原谅比请求允许更容易)。下面的代码是基于python标准

# The A and B dictionaries
A = {'a': 1, 'b': 2, 'c': 3}
B = {'b': 3, 'c': 4, 'd': 5}


# The final dictionary. Will contain the final outputs.
newdict = {}


# Make sure every key of A and B get into the final dictionary 'newdict'.
newdict.update(A)
newdict.update(B)


# Iterate through each key of A.
for i in A.keys():


# If same key exist on B, its values from A and B will add together and
# get included in the final dictionary 'newdict'.
try:
addition = A[i] + B[i]
newdict[i] = addition


# If current key does not exist in dictionary B, it will give a KeyError,
# catch it and continue looping.
except KeyError:
continue

编辑:感谢jerzyk提出的改进建议。

myDict = {}
for k in itertools.chain(A.keys(), B.keys()):
myDict[k] = A.get(k, 0)+B.get(k, 0)
import itertools
import collections


dictA = {'a':1, 'b':2, 'c':3}
dictB = {'b':3, 'c':4, 'd':5}


new_dict = collections.defaultdict(int)
# use dict.items() instead of dict.iteritems() for Python3
for k, v in itertools.chain(dictA.iteritems(), dictB.iteritems()):
new_dict[k] += v


print dict(new_dict)


# OUTPUT
{'a': 1, 'c': 7, 'b': 5, 'd': 5}

你也可以像@Martijn上面提到的那样使用Counter。

要获得更通用和可扩展的方法,请检查mergedict。它使用singledispatch并可以根据其类型合并值。

例子:

from mergedict import MergeDict


class SumDict(MergeDict):
@MergeDict.dispatch(int)
def merge_int(this, other):
return this + other


d2 = SumDict({'a': 1, 'b': 'one'})
d2.merge({'a':2, 'b': 'two'})


assert d2 == {'a': 3, 'b': 'two'}
def merge_with(f, xs, ys):
xs = a_copy_of(xs) # dict(xs), maybe generalizable?
for (y, v) in ys.iteritems():
xs[y] = v if y not in xs else f(xs[x], v)


merge_with((lambda x, y: x + y), A, B)

你可以很容易地概括如下:

def merge_dicts(f, *dicts):
result = {}
for d in dicts:
for (k, v) in d.iteritems():
result[k] = v if k not in result else f(result[k], v)

然后它可以取任意数量的字典。

来自python 3.5:合并和求和

感谢@tokeinizer_fsj,在评论中告诉我,我没有完全得到问题的意义(我认为添加意味着只是添加键,最终在两个字典中不同,相反,我的意思是公共键值应该被求和)。所以我在合并之前添加了这个循环,这样第二个字典就包含了公共键的和。最后一个字典的值将在新字典中存在,新字典是两个字典合并的结果,所以我认为问题已经解决了。该解决方案从python 3.5及以下版本有效。

a = {
"a": 1,
"b": 2,
"c": 3
}


b = {
"a": 2,
"b": 3,
"d": 5
}


# Python 3.5


for key in b:
if key in a:
b[key] = b[key] + a[key]


c = {**a, **b}
print(c)


>>> c
{'a': 3, 'b': 5, 'c': 3, 'd': 5}

可重用代码

a = {'a': 1, 'b': 2, 'c': 3}
b = {'b': 3, 'c': 4, 'd': 5}




def mergsum(a, b):
for k in b:
if k in a:
b[k] = b[k] + a[k]
c = {**a, **b}
return c




print(mergsum(a, b))

这个解决方案很容易使用,它被用作一个普通的字典,但你可以使用求和函数。

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}

当然,在这种情况下,对# eyz0求和是最python化的方法,但只有当结果为正值时。下面是一个例子,正如你所看到的,在对B字典中的c的值求负后,结果中没有c

In [1]: from collections import Counter


In [2]: A = Counter({'a':1, 'b':2, 'c':3})


In [3]: B = Counter({'b':3, 'c':-4, 'd':5})


In [4]: A + B
Out[4]: Counter({'d': 5, 'b': 5, 'a': 1})

这是因为# eyz0主要用于使用正整数来表示运行计数(负计数是没有意义的)。但是为了帮助这些用例,python记录了最小范围和类型限制如下:

    Counter类本身是一个字典 对其键和值没有限制的子类。值为 本意是表示计数的数字,但您可以存储 value字段中的任何值。
  • most_common()方法只需
  • 用于原地操作,例如c[key] += 1,值类型只需要支持加减法。所以分数,浮点数和小数都可以,负值也可以 支持。对于update()subtract()也是如此 允许输入和输出均为负值和零值 multiset方法仅设计用于具有正值的用例。 输入可以为负或零,但输出只能为正 价值被创造出来。没有类型限制,只有值类型 需要支持加法、减法和比较
  • elements()方法需要整数计数。它忽略零和负计数。

所以为了解决这个问题,你可以使用Counter.update来得到你想要的输出。它的工作方式类似于dict.update(),但添加计数而不是替换它们。

In [24]: A.update(B)


In [25]: A
Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1})

这是合并两个字典的简单解决方案,其中+=可以应用于值,它只需要在字典上迭代一次

a = {'a':1, 'b':2, 'c':3}


dicts = [{'b':3, 'c':4, 'd':5},
{'c':9, 'a':9, 'd':9}]


def merge_dicts(merged,mergedfrom):
for k,v in mergedfrom.items():
if k in merged:
merged[k] += v
else:
merged[k] = v
return merged


for dct in dicts:
a = merge_dicts(a,dct)
print (a)
#{'c': 16, 'b': 5, 'd': 14, 'a': 10}

上述解决方案非常适合有少量# eyz0的场景。如果你有一个很大的列表,像这样的就更好了:

from collections import Counter


A = Counter({'a':1, 'b':2, 'c':3})
B = Counter({'b':3, 'c':4, 'd':5})
C = Counter({'a': 5, 'e':3})
list_of_counts = [A, B, C]


total = sum(list_of_counts, Counter())


print(total)
# Counter({'c': 7, 'a': 6, 'b': 5, 'd': 5, 'e': 3})

上面的解决方案本质上是对Counters的求和:

total = Counter()
for count in list_of_counts:
total += count
print(total)
# Counter({'c': 7, 'a': 6, 'b': 5, 'd': 5, 'e': 3})

这个做的是同样的事情,但我认为它总是有助于看到它在下面有效地做了什么。

此外,请注意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

将三个字典a,b,c合并到一行中,没有任何其他模块或库

如果我们有三个字典

a = {"a":9}
b = {"b":7}
c = {'b': 2, 'd': 90}

将所有内容合并为一行,并使用返回一个dict对象

c = dict(a.items() + b.items() + c.items())

返回

{'a': 9, 'b': 2, 'd': 90}

是什么:

def dict_merge_and_sum( d1, d2 ):
ret = d1
ret.update({ k:v + d2[k] for k,v in d1.items() if k in d2 })
ret.update({ k:v for k,v in d2.items() if k not in d1 })
return ret


A = {'a': 1, 'b': 2, 'c': 3}
B = {'b': 3, 'c': 4, 'd': 5}


print( dict_merge_and_sum( A, B ) )

输出:

{'d': 5, 'a': 1, 'c': 7, 'b': 5}

更传统的结合两个字典的方法。使用模块和工具是很好的,但理解背后的逻辑将有助于防止你不记得工具。

程序组合两个字典相加值的公共键。

def combine_dict(d1,d2):


for key,value in d1.items():
if key in d2:
d2[key] += value
else:
d2[key] = value
return d2


combine_dict({'a':1, 'b':2, 'c':3},{'b':3, 'c':4, 'd':5})


output == {'b': 5, 'c': 7, 'd': 5, 'a': 1}

一种解决方法是使用字典理解。

C = { k: A.get(k,0) + B.get(k,0) for k in list(B.keys()) + list(A.keys()) }

这是一个很一般的解。你可以处理任意数量的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}

下面是另一个使用字典推导和dict()行为的选项:

dict3 = dict(dict1, **{ k: v + dict1.get(k, 0) for k, v in dict2.items() })
# {'a': 4, 'b': 2, 'c': 7, 'g': 1}

从# EYZ0:

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。

dict1 = {'a':1, 'b':2, 'c':3}
dict2 = {'a':3, 'g':1, 'c':4}
dict3 = {} # will store new values


for x in dict1:
    

if x in dict2: #sum values with same key
dict3[x] = dict1[x] +dict2[x]
else: #add the values from x to dict1
dict3[x] = dict1[x]
        

#search for new values not in a
for x in dict2:
if x not in dict1:
dict3[x] = dict2[x]
print(dict3) # {'a': 4, 'b': 2, 'c': 7, 'g': 1}