【发布时间】:2021-02-27 11:09:44
【问题描述】:
合并到字典的最有效方法似乎是使用字典解包:
def merge_dicts(x, y)
return {**x, **y}
但是如果你有两个以上的字典呢?如果您不知道您将拥有多少个字典怎么办?
以下内容不受支持,会产生语法错误 (See PEP-448 section Variations):
def merge_dicts(*dicts):
return {**d for d in dicts}
这个替代方案的可读性和性能相当好:
def merge_dicts_comprehension(*dicts):
return {k: v for d in dicts for k, v in d.items()}
但是使用解包仍然更快。例如。如果你知道len(dicts)==5,这会更快:
def merge_dicts_unpack(*dicts):
return {
**dicts[0],
**dicts[1],
**dicts[2],
**dicts[3],
**dicts[4],
}
但是动态怎么做呢?
这是一种方法,但它很可怕:
def merge_dicts_dynamic(*dicts):
num = len(dicts)
func_name = f"_merge_{num}"
try:
func = globals()[func_name]
except KeyError:
spread = ', '.join([f"**d[{x}]" for x in range(num)])
func_text = f'def {func_name}(*d):\n '
func_text += f'return {{{spread}}}'
foo_code = compile(func_text, "<string>", "exec")
func = FunctionType(foo_code.co_consts[0], globals(), func_name)
globals()[func_name] = func
return func(*dicts)
它优于{k: v for d in dicts for k, v in d.items()} 方法。但又一次,可怕。
如何在不动态生成函数的情况下达到与解包相当的性能?
其他变体:
def merge_dicts_unpack_iter(*dicts):
val = {}
for d in dicts:
val = {**val, **d}
return val
回答者变体:
来自@jizhihaoSAMA:
def merge_dicts_reduce(*dicts):
return reduce(lambda x, y: {**x, **y}, dicts)
来自@RonSerruya:
def merge_dicts_update(*dicts):
d = dict()
for x in dicts:
d.update(x)
return d
测试数据:
test_dicts = [{str(x): x for x in range(num, num + 20)} for num in [20, 40, 80, 100, 120]]
测试代码:
for test_func in [
'comprehension',
'unpack_iter',
'dynamic',
'reduce',
'update',
'unpack',
]:
result = timeit.timeit(
stmt=f'merge_dicts_{test_func}(*test_dicts)',
number=1000000,
globals=globals(),
)
print(f"{test_func}: {result}")
结果:
comprehension: 7.904960700000629
unpack_iter: 7.860937651999848
dynamic: 3.4844029209998553
reduce: 8.41494683899964
update: 3.8839738759998
unpack: 3.184907333999945
结论:
我同意 Ron 的观点,即更新版本提供了可读性和性能的最佳折衷。
它的性能不如静态解包或动态解包,但非常接近。
【问题讨论】:
-
重复键应该怎么办?
-
右覆盖左
标签: python python-3.x