是否有任何pythonic方法来组合两个dict(为两个dict中出现的键添加值)?
例如,我有两条格言:是否有任何pythonic方法来组合两个dict(为两个dict中出现的键添加值)?,python,dictionary,Python,Dictionary,例如,我有两条格言: Dict A: {'a': 1, 'b': 2, 'c': 3} Dict B: {'b': 3, 'c': 4, 'd': 5} 我需要一种“结合”两个dict的pythonic方法,结果是: {'a': 1, 'b': 5, 'c': 7, 'd': 5} 也就是说:如果一个键出现在两个dict中,则添加它们的值,如果它只出现在一个dict中,则保留其值。使用: 计数器基本上是dict的一个子类,因此您仍然可以使用它们执行通常使用该类型所执行的所有其他操作,例如迭代
Dict A: {'a': 1, 'b': 2, 'c': 3}
Dict B: {'b': 3, 'c': 4, 'd': 5}
我需要一种“结合”两个dict的pythonic方法,结果是:
{'a': 1, 'b': 5, 'c': 7, 'd': 5}
也就是说:如果一个键出现在两个dict中,则添加它们的值,如果它只出现在一个dict中,则保留其值。使用:
计数器基本上是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版本不会太旧或出现任何问题
然后是最“黑客”的解决方案。它们又大又短,但有时很难理解、阅读和记忆
不过,还有一种选择,那就是尝试重新发明轮子。
-为什么要重新发明轮子?
-一般来说,这是一种非常好的学习方法(有时只是因为现有的工具不能完全满足您的需求和/或您的需求),如果您不知道或不记得解决问题的最佳工具,这是最简单的方法
因此,我建议从集合
模块中重新设计计数器
类的轮子(至少部分):
可能会有其他的方法来实现这一点,而且已经有工具来实现这一点,但是将事情的基本工作方式可视化总是很好的。没有额外导入的方法强> 他们的标准是蟒蛇式的(请求原谅比请求允许更容易)。下面的代码基于该python标准 编辑:感谢他的改进建议
myDict = {}
for k in itertools.chain(A.keys(), B.keys()):
myDict[k] = A.get(k, 0)+B.get(k, 0)
或
或者,您可以使用上面提到的@Martijn计数器。进行更通用和可扩展的方式检查。它使用
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_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)
然后它可以接受任意数量的dict。来自Python3.5:合并和求和
感谢@tokenizer_fsj在一篇评论中告诉我,我没有完全理解这个问题的含义(我认为add意味着只添加最终在两个词汇中不同的键,相反,我的意思是应该将共同的键值相加)。因此,我在合并之前添加了该循环,以便第二个字典包含公共键的总和。最后一个字典的值将在新字典中保持不变,这是两个字典合并的结果,所以我认为问题已经解决了。该解决方案在Python3.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}
可重用代码
此解决方案易于使用,可用作普通字典,但可以使用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}
在这种情况下,对
计数器()
s求和肯定是最适合的方法,但是只有当它得到一个正值时才可以。下面是一个示例,您可以看到,在对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})
这是因为计数器
s主要设计用于使用正整数来表示运行计数(负计数没有意义)。但为了帮助处理这些用例,python将最小范围和类型限制记录如下:
- Counter类本身就是一本字典 对其键和值没有限制的子类。这些值是 用于表示计数的数字,但您可以存储 值字段中的任何内容
方法只需要 这些值是可排序的most_common()
- 用于原位操作,如
c[键]
,值类型只需支持加减运算。所以分数、浮点数和小数都是有效的,而负值是无效的 支持。这同样适用于+=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}
上述解决方案非常适合于使用少量
计数器的场景。如果你有一个大的清单,像这样的东西会更好:
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})
上述解决方案实质上是通过以下方式对计数器进行求和:
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
在一行中合并三个DICT a、b、c,而不使用任何其他模块或LIB
如果我们有三条格言
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}
那么:
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}
您还可以在b.viewkeys()和a.viewkeys()中对k使用,在何时使用,并跳过集合的创建。为什么set(a)
返回键集而不是元组集?为什么要这样做
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 = {"a":9}
b = {"b":7}
c = {'b': 2, 'd': 90}
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}