是否有一种 Pythonic 方法可以用另一个字典扩展一个字典(保留所有值)
Is there a Pythonic way to extend one dictionary with another (preserving all values)
给定字典:
d1={'a':'a','b':'b','c':'c'}
d2={'b':'a','c':['d','f','g'],'e':'e'}
是否可以将这两个词典组合在一起,合并所有公共键,并保留所有值? IE。给出输出:
> print(d1.extend(d2))
{'a':'a','b':['b','a'],'c':['c','d','f','g'],'e':'e'}
我想出了以下方法,它似乎有效,但非常不符合 pythonic。
def extend(d1, d2):
return_dict={}
for key, value in d1.items():
if key in d2:
value_d2=d2[key]
if value == value_d2:
continue
if type(value) == list and type(value_d2) == list:
value.extend(value_d2)
return_dict[key]=value
elif type(value) == list and type(value_d2) != list:
tmp=[value_d2]
tmp.extend(value)
return_dict[key]=tmp
elif type(value) != list and type(value_d2) == list:
tmp=[value]
tmp.extend(value_d2)
return_dict[key]=tmp
elif type(value) != list and type(value_d2) != list:
return_dict[key]=[value] + [value_d2]
else:
return_dict[key]=value
for key, value in d2.items():
if key not in return_dict:
return_dict[key]=value
return return_dict
(最后一个elif应该是else,但我觉得这样更易读)
编辑:
是否可以保留所有键,但删除重复值,而不是保留所有值?即
d1={'a':'a','b':'b','c':'c'}
d2={'b':'b','c':['d','f','g'],'e':'e'}
> print(d1.extend(d2))
{'a':'a','b':'b','c':['c','d','f','g'],'e':'e'}
使用collections.defaultdict
作为临时存储,如下图:
from collections import defaultdict
d1 = {'a': 'a', 'b': 'b', 'c': 'c'}
d2 = {'b': 'a', 'c': ['d', 'f', 'g'], 'e': 'e'}
tmp = defaultdict(list)
for d in [d1, d2]:
for k, v in d.items():
if isinstance(v, list):
tmp[k].extend(v)
else:
tmp[k].append(v)
res = { k : v if len(v) > 1 else v[0] for k, v in tmp.items()}
print(res)
输出
{'a': 'a', 'b': ['b', 'a'], 'c': ['c', 'd', 'f', 'g'], 'e': 'e'}
另一种方法,也使用 defaultdict,是:
tmp1 = defaultdict(list)
tmp2 = defaultdict(list)
tmp1.update(d1)
tmp2.update(d2)
tmp = {key: [*tmp1[key], *tmp2[key]] for key in tmp1.keys() | tmp2.keys()}
res = {k: v if len(v) > 1 else v[0] for k, v in tmp.items()}
print(res)
这两种方法都适用于 Python 3.7.
更新
如@ShadowRanger 所述,您可以使用集合而不是列表:
tmp1 = defaultdict(set)
tmp2 = defaultdict(set)
tmp1.update(d1)
tmp2.update(d2)
tmp = {key: [*tmp1[key], *tmp2[key]] for key in tmp1.keys() | tmp2.keys()}
res = {k: v if len(v) > 1 else v[0] for k, v in tmp.items()}
print(res)
您可以将辅助函数 safe_combine
与 dict.union
运算符 |
结合使用 Python 3.8+:
from __future__ import annotations
d1 = {'a': 'a', 'b': 'b', 'c': 'c'}
d2 = {'b': 'a', 'c': ['d', 'f', 'g'], 'e': 'e'}
def safe_combine(o1: str | list, o2: str | list):
return (o1 if isinstance(o1, list) else [o1]) \
+ (o2 if isinstance(o2, list) else [o2])
merged = {k: safe_combine(d1[k], d2[k]) if k in d1 and k in d2 else v
for k, v in (d1 | d2).items()}
print(merged)
输出:
{'a': 'a', 'b': ['b', 'a'], 'c': ['c', 'd', 'f', 'g'], 'e': 'e'}
注意:对于3.8之前的Python版本,可以使用{**d1, **d2}
语法代替(d1 | d2)
.
给定字典:
d1={'a':'a','b':'b','c':'c'}
d2={'b':'a','c':['d','f','g'],'e':'e'}
是否可以将这两个词典组合在一起,合并所有公共键,并保留所有值? IE。给出输出:
> print(d1.extend(d2))
{'a':'a','b':['b','a'],'c':['c','d','f','g'],'e':'e'}
我想出了以下方法,它似乎有效,但非常不符合 pythonic。
def extend(d1, d2):
return_dict={}
for key, value in d1.items():
if key in d2:
value_d2=d2[key]
if value == value_d2:
continue
if type(value) == list and type(value_d2) == list:
value.extend(value_d2)
return_dict[key]=value
elif type(value) == list and type(value_d2) != list:
tmp=[value_d2]
tmp.extend(value)
return_dict[key]=tmp
elif type(value) != list and type(value_d2) == list:
tmp=[value]
tmp.extend(value_d2)
return_dict[key]=tmp
elif type(value) != list and type(value_d2) != list:
return_dict[key]=[value] + [value_d2]
else:
return_dict[key]=value
for key, value in d2.items():
if key not in return_dict:
return_dict[key]=value
return return_dict
(最后一个elif应该是else,但我觉得这样更易读)
编辑:
是否可以保留所有键,但删除重复值,而不是保留所有值?即
d1={'a':'a','b':'b','c':'c'}
d2={'b':'b','c':['d','f','g'],'e':'e'}
> print(d1.extend(d2))
{'a':'a','b':'b','c':['c','d','f','g'],'e':'e'}
使用collections.defaultdict
作为临时存储,如下图:
from collections import defaultdict
d1 = {'a': 'a', 'b': 'b', 'c': 'c'}
d2 = {'b': 'a', 'c': ['d', 'f', 'g'], 'e': 'e'}
tmp = defaultdict(list)
for d in [d1, d2]:
for k, v in d.items():
if isinstance(v, list):
tmp[k].extend(v)
else:
tmp[k].append(v)
res = { k : v if len(v) > 1 else v[0] for k, v in tmp.items()}
print(res)
输出
{'a': 'a', 'b': ['b', 'a'], 'c': ['c', 'd', 'f', 'g'], 'e': 'e'}
另一种方法,也使用 defaultdict,是:
tmp1 = defaultdict(list)
tmp2 = defaultdict(list)
tmp1.update(d1)
tmp2.update(d2)
tmp = {key: [*tmp1[key], *tmp2[key]] for key in tmp1.keys() | tmp2.keys()}
res = {k: v if len(v) > 1 else v[0] for k, v in tmp.items()}
print(res)
这两种方法都适用于 Python 3.7.
更新
如@ShadowRanger 所述,您可以使用集合而不是列表:
tmp1 = defaultdict(set)
tmp2 = defaultdict(set)
tmp1.update(d1)
tmp2.update(d2)
tmp = {key: [*tmp1[key], *tmp2[key]] for key in tmp1.keys() | tmp2.keys()}
res = {k: v if len(v) > 1 else v[0] for k, v in tmp.items()}
print(res)
您可以将辅助函数 safe_combine
与 dict.union
运算符 |
结合使用 Python 3.8+:
from __future__ import annotations
d1 = {'a': 'a', 'b': 'b', 'c': 'c'}
d2 = {'b': 'a', 'c': ['d', 'f', 'g'], 'e': 'e'}
def safe_combine(o1: str | list, o2: str | list):
return (o1 if isinstance(o1, list) else [o1]) \
+ (o2 if isinstance(o2, list) else [o2])
merged = {k: safe_combine(d1[k], d2[k]) if k in d1 and k in d2 else v
for k, v in (d1 | d2).items()}
print(merged)
输出:
{'a': 'a', 'b': ['b', 'a'], 'c': ['c', 'd', 'f', 'g'], 'e': 'e'}
注意:对于3.8之前的Python版本,可以使用{**d1, **d2}
语法代替(d1 | d2)
.