有没有什么pythonic方式来结合两个字典(增加键出现在两个值)?

比如我有两个字谜:

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

我需要一个pythonic的方式来“结合”两个字母,结果是:

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

也就是说:如果一个关键字出现在两个字典中,那么添加它们的值,如果它只出现在一个字典中,则保留它的值。

使用collections.Counter

 >>> 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一个子类,所以你仍然可以用他们通常用这种types做的所有事情,比如遍历它们的键和值。

更通用的解决scheme,也适用于非数字值:

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

简介:有(可能)最好的解决scheme。 但是你必须知道它并且记住它,有时你必须希望你的Python版本不是太旧,或者不pipe什么问题。

那么最“黑客”的解决scheme。 他们是伟大而短暂的,但有时很难理解,阅读和记忆。

不过,还有一种方法是试图重新发明轮子。 – 为什么重新发明轮子? – 通常是因为这是一个非常好的学习方法(有时候只是因为已经存在的工具不能完全按照你想要的和/或你喜欢的方式来做)以及最简单的方法,如果你不知道或不记得你的问题的完美工具。

所以 ,我build议从collections模块(至less部分)重新创buildCounter类的轮子:

 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} 

可能还有其他方法可以实现这一点,并且已经有了一些工具可以实现这一点,但总是很好地展示事物的基本工作原理。

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

一个没有额外的import!

他们是一个称为EAFP的pythonic 标准 (容易要求宽恕而不是权限)。 下面的代码是基于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 

编辑:谢谢他的改进build议jerzyk 。

 import itertools import collections dictA = {'a':1, 'b':2, 'c':3} dictB = {'b':3, 'c':4, 'd':5} new_dict = collections.defaultdict(int) 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} 

要么

另外你可以使用Counter作为@Martijn上面提到的。

对于更通用和可扩展的方式来检查mergedict 。 它使用了singledispatch并可以根据其types合并值。

例:

 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'} 

在这种情况下, Counter()的绝对加和是最好的方法,但是只有当结果为正值时 。 这里是一个例子,你可以看到在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}) 

这是因为Counter主要被devise成用正整数来表示运行计数(负计数是毫无意义的)。 但为了帮助这些用例,pythonlogging了最小范围和types限制,如下所示:

  • Counter类本身是一个字典子类,对其键和值没有限制。 这些值旨在表示计数的数字,但您可以在值字段中存储任何内容。
  • most_common()方法只要求值是可订购的。
  • 对于诸如c[key] += 1的就地操作,值types只需要支持加法和减法。 所以分数,浮点数和小数将起作用,负值被支持。 update()subtract()同样适用于input和输出都为负值和零值的情况。
  • multiset方法仅为具有正值的用例而devise。 input可能是负数或零,但只有正值的输出被创build。 没有types限制,但值types需要支持加,减,比较。
  • elements()方法需要整数。 它忽略零和负数。

所以为了解决这个问题,在总结你的计数器之后,你可以使用Counter.update来获得所需的输出。 它像dict.update()一样工作,但增加了计数而不是replace它们。

 In [24]: A.update(B) In [25]: A Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1}) 
 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) 

那么它可以采取任何数量的字典。

这是我的方式:

 dic1 = {'one':1,'two':2} dic2 = {'three':3,'four':4} dic1U2 = {} for i in dic1.keys(): dic1U2[i] = dic1[i] for i in dic2.keys(): dic1U2[i] = dic2[i] print(dic1U2) 

这个解决scheme很容易使用,它被用作一个正常的字典,但是你可以使用sum函数。

 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 

这是一个简单的解决scheme,用于合并两个字典,其中+=可以应用于值,它只需要遍历字典一次,我很惊讶没有人提出这个

 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} 

上面的解决scheme对于你有less量Counter的情况非常Counter 。 如果你有他们的大名单,这样的事情更好:

 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}) 

上面的解决scheme实质上是通过以下方式将Counter相加:

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

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

最好使用的是dict():

 A = {'a':1, 'b':2, 'c':3} B = {'b':3, 'c':4, 'd':5} Merged = dict(A, **B) Merged == {'a':1, 'b':3, 'c':3, 'd':5}