是否有任何 Python 方式来合并两个字典(为同时出现在两个字典中的键添加值)?

2024-12-05 08:37:00
admin
原创
137
摘要:问题描述:例如我有两个字典: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}

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


解决方案 1:

使用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,因此您仍然可以对它们执行通常对该类型执行的所有其他操作,例如迭代它们的键和值。

解决方案 2:

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

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}

解决方案 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}

解决方案 4:

简介:
有(可能)最好的解决方案。但你必须知道并记住它,有时你还得祈祷你的 Python 版本不要太旧,否则可能会出现问题。

然后是最“黑客”的解决方案。它们很棒而且很简短,但有时很难理解、阅读和记住。

但是,还有另一种选择,那就是尝试重新发明轮子。 - 为什么要重新发明轮子? - 一般来说,因为这是一种非常好的学习方法(有时只是因为已经存在的工具不能完全满足您的要求和/或按照您希望的方式完成),如果您不知道或不记得最适合您的问题的工具,那么这是最简单的方法。

因此Counter,我建议从模块的角度重新发明类的轮子collections(至少是部分地):

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}

可能还有其他方法来实现这一点,而且已经有工具可以做到这一点,但直观地了解事情的基本运作方式总是好的。

解决方案 5:

在这种情况下,对 s求和无疑Counter()是最符合 Python 风格的处理方式,但前提是求和的结果为正值。下面是一个例子,你可以看到,c在对字典c中的 s 值求反后,结果为零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})

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

  • Counter 类本身是一个字典子类,其键和值没有任何限制。值应为表示计数的数字,但您可以在值字段中存储任何内容。

  • most_common()方法仅要求值是可排序的。

  • 对于 等就地运算c[key]
    += 1,值类型只需支持加法和减法。因此分数、浮点数和小数都可以使用,并且支持负值。 和 也是如此update()subtract()它们允许输入和输出为负值和零值。

  • 多集方法仅针对具有正值的用例而设计。输入可以是负数或零,但只创建具有正值的输出。没有类型限制,但值类型需要支持加法、减法和比较。

  • elements()方法需要整数计数。它会忽略零和负计数。

因此,为了解决这个问题,在对计数器求和后,您可以使用它Counter.update来获得所需的输出。它的工作原理类似于,dict.update()但会添加计数而不是替换它们。

In [24]: A.update(B)

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

解决方案 6:

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

解决方案 7:

沒有额外進口的那一个

他们有一个Python 标准,称为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提出的改进建议。

解决方案 8:

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}

或者

或者,您可以使用 Counter,正如@Martijn 上面提到的。

解决方案 9:

对于更通用和可扩展的方法,请查看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'}

解决方案 10:

来自 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))

解决方案 11:

此外,请注意,a.update( b )它比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

解决方案 12:

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

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

解决方案 13:

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)

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

解决方案 14:

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

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}

解决方案 15:

上述解决方案非常适合您拥有少量Counters 的情况。但是,如果您拥有大量 s 列表,那么类似下面的方法会更好:

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

上述解决方案本质上是通过以下方式Counter对 s 进行求和:

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

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

解决方案 16:

下面是使用字典推导与行为相结合的另一种选择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。

解决方案 17:

这个解决方案很容易使用,它可以用作普通字典,但您可以使用 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}

解决方案 18:

那么:

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}

解决方案 19:

更传统的组合两个字典的方法。使用模块和工具是好的,但理解其背后的逻辑会有所帮助,以防你记不住工具。

将两个字典合并起来,并为公共键添加值。

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}

解决方案 20:

这是一个非常通用的解决方案。您可以处理任意数量的字典 + 仅存在于某些字典中的键 + 轻松使用您想要的任何聚合函数:

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}

解决方案 21:

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}

解决方案 22:

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

如果我们有三个字典

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

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

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

返回

{'a': 9, 'b': 2, 'd': 90}
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用