是否有任何pythonic方法来组合两个dicts(为两个键中出现的键添加值)?

Der*_*ang 460 python dictionary

例如,我有两个词:

Dict A: {'a': 1, 'b': 2, 'c': 3}
Dict B: {'b': 3, 'c': 4, 'd': 5}
Run Code Online (Sandbox Code Playgroud)

我需要一种pythonic方式"组合"两个dicts,结果是:

{'a': 1, 'b': 5, 'c': 7, 'd': 5}
Run Code Online (Sandbox Code Playgroud)

也就是说:如果两个词中都出现一个键,则添加它们的值,如果它只出现在一个词典中,则保持其值.

Mar*_*ers 820

用途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})
Run Code Online (Sandbox Code Playgroud)

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

  • @ Jan-PhilipGehrcke:给`sum()`一个起始值,使用`sum(计数器,Counter())`. (27认同)
  • @ Jan-PhilipGehrcke:你的另一个选择是使用循环和`+ =`来进行就地求和.`res = counters [0]`,然后`for c in counter [1:]:res + = c`. (6认同)
  • 谢谢.但是,这个方法受到中间对象创建的影响,因为求和字符串是,对吧? (5认同)
  • 什么有多个计数器像这样合并?不幸的是,`sum(counter)`不起作用. (3认同)
  • 我喜欢这种方法!如果有人喜欢保持接近处理字典的东西,那么也可以在计数器[1:]:res.update(c)中使用`update()`而不是`+ =`:`来表示c. (3认同)

geo*_*org 118

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

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)])
Run Code Online (Sandbox Code Playgroud)

甚至更通用:

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)])
Run Code Online (Sandbox Code Playgroud)

例如:

>>> 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}
Run Code Online (Sandbox Code Playgroud)

  • 你也可以在[使用python 2.7](http://code.activestate.com/recipes/576611-counter-class/)中使用`for b in b.viewkeys()&a.viewkeys()`,并跳过集的创建. (27认同)
  • 你能添加一个兼容Python 3的选项吗?对于a.keys()&b}}中的k,`{**a,**b,**{k:op(a [k],b [k])`应该适用于Python 3.5+. (5认同)
  • @Craicerjack:是的,我使用`operator.mul`来表明这段代码是通用的,不仅限于添加数字. (2认同)

Ash*_*ary 67

>>> 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}
Run Code Online (Sandbox Code Playgroud)

  • `set(A)`与`set(A.keys())`相同,所以你可以将调用放到`.keys()`. (16认同)
  • ...在python 2.x中,执行`set(A)`比执行`set(A.keys())`要快一些,因为你避免创建调用`keys()`产生的额外序列(使用`set(A)`只是使`A`将迭代器对象返回到`set()`). (5认同)

Jer*_*meJ 45

简介: 有(可能)最佳解决方案.但你必须知道它并记住它,有时候你不得不希望你的Python版本不是太旧或者问题可能是什么.

然后是最"讨厌"的解决方案.它们很棒而且很短,但有时很难理解,阅读和记忆.

但是,有一种方法可以尝试重新发明轮子. - 为什么重新发明轮子? - 通常因为这是一种非常好的学习方式(有时仅仅因为已经存在的工具并不能完全按照您的意愿和/或您希望的方式完成)而且最简单的方法是如果你不知道或者不记得你的问题的完美工具.

所以,我建议Countercollections模块中重新发明类的轮子(部分至少):

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}
Run Code Online (Sandbox Code Playgroud)

可能还有其他方法可以实现它,并且已经有工具可以做到这一点,但总是很好地可视化事物基本上如何工作.

  • 对于我们这些仍然在2.6的人来说也不错 (3认同)

Dev*_*ini 14

没有额外进口的那个!

它们是一种名为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
Run Code Online (Sandbox Code Playgroud)

编辑:感谢jerzyk的改进建议.

  • n ^ 2算法将明显慢于Counter方法 (5认同)

小智 14

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


小智 11

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}
Run Code Online (Sandbox Code Playgroud)

要么

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


Kas*_*mvd 11

绝对地总结Counter()s是在这种情况下最狡猾的方式,但只有当它产生正值时.这是一个例子,你可以看到c在否定字典中c的值之后没有结果B.

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})
Run Code Online (Sandbox Code Playgroud)

这是因为Counters主要用于处理正整数来表示运行计数(负计数无意义).但是为了帮助解决这些用例,python记录了最小范围和类型限制,如下所示:

  • Counter类本身是一个字典子类,其键和值没有限制.这些值旨在表示计数,但您可以在值字段中存储任何内容.
  • 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})
Run Code Online (Sandbox Code Playgroud)


sch*_*o72 7

对于更通用和可扩展的方式,请检查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'}
Run Code Online (Sandbox Code Playgroud)


Gio*_* PY 6

从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}
Run Code Online (Sandbox Code Playgroud)

可重用代码

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))
Run Code Online (Sandbox Code Playgroud)


小智 6

一行解决方案是使用字典理解。

C = { k: A.get(k,0) + B.get(k,0) for k in list(B.keys()) + list(A.keys()) }
Run Code Online (Sandbox Code Playgroud)


sho*_*see 5

此外,请注意a.update( b )比 快 2 倍a + b

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
Run Code Online (Sandbox Code Playgroud)