我想合并两个词典A和B,结果包含:
例如:
def f(x, y):
return x * y
A = {1:1, 2:3}
B = {7:3, 2:2}
C = merge(A, B)
输出:
{1:1, 7:3, 2:6}
感觉应该有一个很好的单线来做到这一点。
答案 0 :(得分:6)
使用dictionary views来实现这一目标; dict.viewkeys()
result就像一个集合,让你做交叉和对称的差异:
def merge(A, B, f):
# Start with symmetric difference; keys either in A or B, but not both
merged = {k: A.get(k, B.get(k)) for k in A.viewkeys() ^ B.viewkeys()}
# Update with `f()` applied to the intersection
merged.update({k: f(A[k], B[k]) for k in A.viewkeys() & B.viewkeys()})
return merged
在Python 3中,.viewkeys()
方法已重命名为.keys()
,取代旧的.keys()
功能(在Python 2中重新编写列表)。
上述merge()
方法是适用于任何给定f()
的通用解决方案。
演示:
>>> def f(x, y):
... return x * y
...
>>> A = {1:1, 2:3}
>>> B = {7:3, 2:2}
>>> merge(A, B, f)
{1: 1, 2: 6, 7: 3}
>>> merge(A, B, lambda a, b: '{} merged with {}'.format(a, b))
{1: 1, 2: '3 merged with 2', 7: 3}
答案 1 :(得分:1)
从@MartijnPieters
中窃取此A.get(k, B.get(k))
代码段
>>> def f(x, y):
return x * y
>>> A = {1:1, 2:3}
>>> B = {7:3, 2:2}
>>> {k: f(A[k], B[k]) if k in A and k in B else A.get(k, B.get(k))
for k in A.viewkeys() | B.viewkeys()}
{1: 1, 2: 6, 7: 3}
答案 2 :(得分:1)
这是我在Python 3中针对一般情况的解决方案代码。
我首先编写了 merge 函数,然后将其扩展到更通用的 merge_with 函数,该函数接受函数和各种数量的字典。如果这些词典中有任何重复的键,请将提供的函数应用于键重复的值。
merge 函数可以使用 merge_with 函数重新定义,就像 merger 函数一样。名称 merger 意味着将它们全部合并并保留最右边的值,是否有重复项。 mergel 函数也是如此,它保留最左边的。
此处的所有功能 - 合并, merge_with , mergel 和 merger - 在这种情况下都是通用的他们采用任意数量的字典参数。具体来说, merge_with 必须将与其应用的数据兼容的函数作为参数。
from functools import reduce
from operator import or_
def merge(*dicts):
return { k: reduce(lambda d, x: x.get(k, d), dicts, None)
for k in reduce(or_, map(lambda x: x.keys(), dicts), set()) }
def merge_with(f, *dicts):
return { k: (lambda x: f(*x) if len(x)>1 else x[0])([ d[k] for d in dicts
if k in d ])
for k in reduce(or_, map(lambda x: x.keys(), dicts), set()) }
mergel = lambda *dicts: merge_with(lambda *x: x[0], *dicts)
merger = lambda *dicts: merge_with(lambda *x: x[-1], *dicts)
测试
>>> squares = { k:k*k for k in range(4) }
>>> squares
{0: 0, 1: 1, 2: 4, 3: 9}
>>> cubes = { k:k**3 for k in range(2,6) }
>>> cubes
{2: 8, 3: 27, 4: 64, 5: 125}
>>> merger(squares, cubes)
{0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125}
>>> merger(cubes, squares)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 64, 5: 125}
>>> mergel(squares, cubes)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 64, 5: 125}
>>> mergel(cubes, squares)
{0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125}
>>> merge(squares, cubes)
{0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125}
>>> merge(cubes, squares)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 64, 5: 125}
>>> merge_with(lambda x, y: x+y, squares, cubes)
{0: 0, 1: 1, 2: 12, 3: 36, 4: 64, 5: 125}
>>> merge_with(lambda x, y: x*y, squares, cubes)
{0: 0, 1: 1, 2: 32, 3: 243, 4: 64, 5: 125}
<强>更新强>
在我写完上述内容后,我发现还有另一种方法可以做到。
from functools import reduce
def merge(*dicts):
return reduce(lambda d1, d2: reduce(lambda d, t:
dict(list(d.items())+[t]),
d2.items(), d1),
dicts, {})
def merge_with(f, *dicts):
return reduce(lambda d1, d2: reduce(lambda d, t:
dict(list(d.items()) +
[(t[0], f(d[t[0]], t[1])
if t[0] in d else
t[1])]),
d2.items(), d1),
dicts, {})
mergel = lambda *dicts: merge_with(lambda x, y: x, *dicts)
merger = lambda *dicts: merge_with(lambda x, y: y, *dicts)
请注意,使用 merge_with 的 mergel 和 merger 的定义已更改为新函数作为第一个参数。 f 函数现在必须是二进制的。上面提供的测试仍然有效。以下是一些测试,以显示这些功能的一般性。
>>> merge() == {}
True
>>> merge(squares) == squares
True
>>> merge(cubes) == cubes
True
>>> mergel() == {}
True
>>> mergel(squares) == squares
True
>>> mergel(cubes) == cubes
True
>>> merger() == {}
True
>>> merger(squares) == squares
True
>>> merger(cubes) == cubes
True
>>> merge_with(lambda x, y: x+y, squares, cubes, squares)
{0: 0, 1: 2, 2: 16, 3: 45, 4: 64, 5: 125}
>>> merge_with(lambda x, y: x*y, squares, cubes, squares)
{0: 0, 1: 1, 2: 128, 3: 2187, 4: 64, 5: 125}
答案 3 :(得分:0)
>>> def f(x,y):
... return x*y
...
>>> dict([(k,v) for k,v in A.items()] + [ (k,v) if k not in A else (k,f(A[k],B[k])) for k,v in B.items()])
{1: 1, 2: 6, 7: 3}
答案 4 :(得分:0)
from itertools import chain
intersection = set(A.keys()).intersection(B.keys())
C = dict(chain(A.items(), B.items(), ((k, f(A[k], B[k])) for k in intersection)))
技术上可以制成一个oneliner。 适用于Py2和Py3。如果您只关心Py3,可以将“交叉”行重写为:
intersection = A.keys() & B.keys()
(仅限Py2,请改用A.viewkeys() & B.viewkeys()
。)
答案 5 :(得分:0)
dict(list(A.items()) + list(B.items()) + [(k,f(A[k],B[k])) for k in A.keys() & B.keys()])
在我看来,是Python 3中最短,最易读的代码。我从DhruvPathak的答案中得出它,并意识到优化它会导致kampu专门针对Python 3的答案:
dict(itertools.chain(A.items(), B.items(), ((k,f(A[k],B[k])) for k in A.keys() & B.keys())))
我在这里比较了所有答案的性能,并获得了这个排名:
mergeLZ: 34.0ms
(赵雷,笨重的单行)mergeJK: 11.6ms
(jamylak)mergeMP: 11.5ms
(Martijn Pieters,几乎是一个单行)mergeDP: 6.9ms
(DhruvPathak)mergeDS: 6.8ms
(上面第一个单行)mergeK3: 5.2ms
(kampu =上面的第二个单线)mergeS3: 3.5ms
(势在必行,不是单行)后者mergeS3是一个天真的,命令式的多行代码。我很失望,在性能方面,旧方式占主导地位。此测试适用于简单的整数键和值,但对于大字符串键和值,排名非常相似。显然里程可能因字典大小和密钥重叠量而异(我的测试中为1/3)。顺便说一句,雷钊的第二次实施,我没有试过去理解,似乎表现糟糕,慢了1000倍。
代码: 导入functools import itertools 进口经营者 导入时间
def t(x): # transform keys and values
return x # str(x) * 8
def f(x,y): # merge values
return x + y
N = 10000
A = {t(k*2): t(k*22) for k in range(N)}
B = {t(k*3): t(k*33) for k in range(N)}
def check(AB):
assert(len(A) == N)
assert(len(B) == N)
assert(len(AB) == 16666)
assert(AB[t(0)] == f(t(0), t(0)))
assert(t(1) not in AB)
assert(AB[t(2)] == t(1*22))
assert(AB[t(3)] == t(1*33))
assert(AB[t(4)] == t(2*22))
assert(t(5) not in AB)
assert(AB[t(6)] == f(t(3*22), t(2*33)))
assert(t(7) not in AB)
assert(AB[t(8)] == t(4*22))
assert(AB[t(9)] == t(3*33))
def mergeLZ(): # Lei Zhao
merged = {k: (lambda x: f(*x) if len(x)>1 else x[0])([ d[k] for d in [A, B]
if k in d ])
for k in functools.reduce(operator.or_, map(lambda x: x.keys(), [A, B]), set()) }
check(merged)
def mergeJK(): # jamylak
merged = {k: f(A[k], B[k]) if k in A and k in B else A.get(k, B.get(k)) for k in A.keys() | B.keys()}
check(merged)
def mergeMP(): # Martijn Pieters
merged = {k: A.get(k, B.get(k)) for k in A.keys() ^ B.keys()}
merged.update({k: f(A[k], B[k]) for k in A.keys() & B.keys()})
check(merged)
def mergeDP(): # DhruvPathak
merged = dict([(k,v) for k,v in A.items()] + [ (k,v) if k not in A else (k,f(A[k],B[k])) for k,v in B.items()])
check(merged)
def mergeDS(): # more elegant (IMO) variation on DhruvPathak
merged = dict(list(A.items()) + list(B.items()) + [(k,f(A[k],B[k])) for k in A.keys() & B.keys()])
check(merged)
def mergeK3(): # kampu adapted to Python 3
merged = dict(itertools.chain(A.items(), B.items(), ((k,f(A[k],B[k])) for k in A.keys() & B.keys())))
check(merged)
def mergeS3(): # "naive" imperative way
merged = A.copy()
for k,v in B.items():
if k in A:
merged[k] = f(A[k], v)
else:
merged[k] = v
check(merged)
for m in [mergeLZ, mergeJK, mergeMP, mergeDP, mergeDS, mergeK3, mergeS3]:
print("{}: {:4.1f}ms".format(m.__name__, timeit.timeit(m, number=1000)))
答案 6 :(得分:0)
对于来自函数式编程背景的用户而言,(imho)更具可读性的不同方法
def merge_with(f):
def merge(a,b):
g = lambda l: [x for x in l if x is not None]
keys = a.keys() | b.keys()
return {key:f(*g([a.get(key), b.get(key)])) for key in keys}
return merge
将此应用于OP的示例:
A = {1:1, 2:3}
B = {7:3, 2:2}
merge_with(lambda x,y=1: x*y)(A,B)
答案 7 :(得分:-1)
def merge_dict(dict1,dict2):
dict1={1:'red'}
dict2={2:'black',3:'yellow'}
dict1.update(dict2)
print 'dict3 =',dict1
merge_dict(dict1,dict2)
<强>输出:强>
dict3 = {1: 'red', 2: 'black', 3: 'yellow'}