如何在Python中使用单个表达式合并两个字典?

6858

我想将两个字典合并成一个新字典。

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
z = merge(x, y)

>>> z
{'a': 1, 'b': 3, 'c': 4}

每当一个键 k 存在于两个字典中时,只有值 y[k] 应该被保留。

43个回答

8978
如何在一行代码中合并两个Python字典?
对于字典x和y,它们的浅层合并字典z从y中获取值,并替换x中的值。
  • In Python 3.9.0 or greater (released 17 October 2020, PEP-584, discussed here):

    z = x | y
    
  • In Python 3.5 or greater:

    z = {**x, **y}
    
  • In Python 2, (or 3.4 or lower) write a function:

    def merge_two_dicts(x, y):
        z = x.copy()   # start with keys and values of x
        z.update(y)    # modifies z with keys and values of y
        return z
    

    and now:

    z = merge_two_dicts(x, y)
    

说明

假设您有两个字典,想要将它们合并为一个新字典,而不改变原始字典:

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}

期望的结果是获得一个新的字典(z),将值合并在一起,并且第二个字典的值会覆盖第一个字典的值。
>>> z
{'a': 1, 'b': 3, 'c': 4}

这是一种新的语法,由PEP 448提出,并且自Python 3.5版本开始可用,其形式为:

z = {**x, **y}

我是一名有用的助手,可以为您进行文本翻译。

请注意,这确实是一个单一表达式。

请注意,我们也可以将其与文字表示法合并:

z = {**x, 'foo': 1, 'bar': 2, **y}

现在:

>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}

现在已经在Python 3.5版本发布计划,PEP 478中实现,并且已经加入到Python 3.5新特性文档中。

然而,由于许多组织仍在使用Python 2,您可能希望以向后兼容的方式进行操作。经典的Pythonic方式,在Python 2和Python 3.0-3.4中可用,是将其作为两个步骤进行:

z = x.copy()
z.update(y) # which returns None since it mutates z

在这两种方法中,y 将会排在第二位,并且它的值将取代 x 的值,因此,在最终结果中,b 将指向 3

尚未使用 Python 3.5,但希望使用单个表达式

如果你还没有使用 Python 3.5 或需要编写向后兼容的代码,并且你想要使用单个表达式,最优的正确方法是将其放在一个函数中:

def merge_two_dicts(x, y):
    """Given two dictionaries, merge them into a new dict as a shallow copy."""
    z = x.copy()
    z.update(y)
    return z

然后你有一个单一的表达式:

z = merge_two_dicts(x, y)

你可以编写一个函数来合并任意数量的字典,从零到非常大的数量:
def merge_dicts(*dict_args):
    """
    Given any number of dictionaries, shallow copy and merge into a new dict,
    precedence goes to key-value pairs in latter dictionaries.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

这个函数适用于所有字典,在Python 2和3中都可以使用。例如,给定字典a到g:
z = merge_dicts(a, b, c, d, e, f, g) 

在字典 af 中,键值对将被字典 g 中的键值对覆盖。

其他答案的批评

不要参考以前被接受的答案:

z = dict(x.items() + y.items())

在Python 2中,为每个字典创建两个列表,创建第三个列表的长度等于前两个列表的长度,然后丢弃所有三个列表来创建字典。但是,在Python 3中,这样做会失败,因为你正在将两个dict_items对象相加,而不是两个列表。
>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'

你需要明确地将它们创建为列表,例如 z = dict(list(x.items()) + list(y.items()))。这会浪费资源和计算能力。

同样,在 Python 3 中使用 items() 的并集(在 Python 2.7 中使用 viewitems())也会在值为不可哈希对象时失败(例如列表)。即使您的值是可哈希的,由于集合在语义上是无序的,因此在优先级方面行为未定义。所以不要这样做:

>>> c = dict(a.items() | b.items())

这个示例展示了当值是不可哈希对象时会发生什么:
>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

这是一个例子,其中y应具有优先权,但由于集合的任意顺序而保留了来自x的值。
>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

另一个不应该使用的黑客技巧:

z = dict(x, **y)

这里使用了dict构造器,非常快速和内存效率高(甚至比我们的两步法稍微更高),但是除非你确切知道这里正在发生什么(也就是说,第二个dict被传递为关键字参数到dict构造器中),否则很难理解,这不是Pythonic的意图和用法。
以下是在Django中 调整 用例的示例。
字典旨在接受可散列键(例如frozensets或元组),但是当键不是字符串时,这种方法在Python 3中会失败
>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

来自邮件列表的创造者Guido van Rossum写道:

我同意宣布dict({}, **{1:3})非法,因为毕竟这是对**机制的滥用。

还有:

显然,dict(x, **y)作为“酷炫技巧”正在流传,用于“调用x.update(y)并返回x”。就个人而言,我发现它比酷炫更卑鄙。

我理解(以及创造者的理解)dict(**y)的预期用途是为了创建可读性更好的字典,例如:

dict(a=1, b=10, c=11)

替代

{'a': 1, 'b': 10, 'c': 11}

评论回复

尽管Guido说过,dict(x, **y)符合字典规范,并且适用于Python 2和3。这仅适用于字符串键是关键参数的直接结果,而不是字典的缺陷。在这个位置使用**操作符也不是机制的滥用,事实上,**恰好是为了将字典作为关键字传递而设计的。

同样,在键不是字符串时,它不能在3中工作。隐式调用契约是命名空间采用普通字典,而用户必须只传递字符串关键字参数。所有其他可调用对象都强制执行此规定。dict在Python 2中打破了这种一致性:

>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}

考虑到Python的其他实现(PyPy,Jython,IronPython),这种不一致性非常糟糕。因此,在Python 3中进行了修复,因为这种用法可能会导致破坏性变化。

我认为故意编写只在语言的一个版本中或只在给定某些任意约束条件下工作的代码是恶意的无能。

更多评论:

dict(x.items() + y.items())仍然是Python 2中最易读的解决方案。可读性很重要。

我的回应是:如果我们真正关心可读性,merge_two_dicts(x, y)对我来说实际上更清晰。并且它不具备向前兼容性,因为Python 2越来越被弃用。

{**x, **y}似乎不能处理嵌套字典。嵌套键的内容只是被覆盖,而不是合并[...]我最终被这些没有递归合并的答案所困扰,我很惊讶没有人提到过这一点。在我对“合并”一词的理解中,这些答案描述的是“使用另一个字典更新一个字典”,而不是合并。

是的。我必须回到问题,它要求对两个字典进行浅层合并,第一个字典的值被第二个字典覆盖 - 用单个表达式。
假设有两个字典的字典,可以在单个函数中递归地合并它们,但你应该注意不要修改任何源字典,并且确保在分配值时进行复制。由于键必须是可哈希的并且通常是不可变的,因此复制它们是毫无意义的:
from copy import deepcopy

def dict_of_dicts_merge(x, y):
    z = {}
    overlapping_keys = x.keys() & y.keys()
    for key in overlapping_keys:
        z[key] = dict_of_dicts_merge(x[key], y[key])
    for key in x.keys() - overlapping_keys:
        z[key] = deepcopy(x[key])
    for key in y.keys() - overlapping_keys:
        z[key] = deepcopy(y[key])
    return z

使用方法:

>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}

这个问题涉及到其他类型的值的备选方案已经超出了本问题的范畴,所以我将指向我在“字典合并”规范问题上的答案

效率较低但正确的临时解决方案

这些方法虽然正确,但效率较低。它们比copyupdate或新的解包方法的迭代抽象层次更高,因为它们需要逐个遍历每个键值对,但是它们确实遵循优先顺序(后面的字典具有优先权)。

你也可以在字典推导式中手动链接字典:

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

或者在Python 2.6中(可能早在引入生成器表达式的2.4版本中):

dict((k, v) for d in dicts for k, v in d.items()) # iteritems in Python 2

itertools.chain会按正确的顺序链接键值对的迭代器:

from itertools import chain
z = dict(chain(x.items(), y.items())) # iteritems in Python 2

性能分析

我只会对已知行为正确的使用情况进行性能分析。(自包含,您可以复制并粘贴自己。)

from timeit import repeat
from itertools import chain

x = dict.fromkeys('abcdefg')
y = dict.fromkeys('efghijk')

def merge_two_dicts(x, y):
    z = x.copy()
    z.update(y)
    return z

min(repeat(lambda: {**x, **y}))
min(repeat(lambda: merge_two_dicts(x, y)))
min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()}))
min(repeat(lambda: dict(chain(x.items(), y.items()))))
min(repeat(lambda: dict(item for d in (x, y) for item in d.items())))

在Python 3.8.1,NixOS中:
>>> min(repeat(lambda: {**x, **y}))
1.0804965235292912
>>> min(repeat(lambda: merge_two_dicts(x, y)))
1.636518670246005
>>> min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()}))
3.1779992282390594
>>> min(repeat(lambda: dict(chain(x.items(), y.items()))))
2.740647904574871
>>> min(repeat(lambda: dict(item for d in (x, y) for item in d.items())))
4.266070580109954

$ uname -a
Linux nixos 4.19.113 #1-NixOS SMP Wed Mar 25 07:06:15 UTC 2020 x86_64 GNU/Linux

字典资源


30
“仅限于字符串”("strings only")只适用于可调用对象中的关键字参数扩展,而不是广义解包语法。为了证明这一点,可以使用以下示例: {**{(0, 1):2}}{(0, 1): 2} - Russia Must Remove Putin
22
如果PEP-0584被接受,这可能会改变。一种新的联合运算符将使用以下语法实现:x | y - Callam Delaney
5
@cal97g 是的,我在大约10天前回答中提到了这一点:https://stackoverflow.com/posts/26853961/revisions - Russia Must Remove Putin
17
嗨,顶部是摘要,是的。由你决定。整个内容可以成为一篇很棒的博客文章。请注意,Python 3.4及以下版本已经停止支持,而3.5版本将在2020年9月逐渐停止支持(EOL)。 - Gringo Suave
25
我同意想要放弃旧方式的渴望,但有时人们必须在只有较老技术可用的环境中工作。人们还必须更新代码,并且看到旧方法与新方法并存可以让他们有信心用相等的新代码替换旧代码。我愿意听取关于重新组织材料的建议,但我认为我们需要保留较老的信息。 - Russia Must Remove Putin
显示剩余21条评论

1813

在您的情况下,您可以执行:

z = dict(list(x.items()) + list(y.items()))

按照您的要求,这将把最终的字典放在 z 中,并使键 b 的值被第二个字典 (y) 的值正确地覆盖:

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

如果您使用Python 2,甚至可以删除list()调用。要创建z:

>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

如果您使用的是 Python 版本 3.9.0a4 或更高版本,您可以直接使用以下代码:

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = x | y
>>> z
{'a': 1, 'c': 11, 'b': 10}

19
不要使用此方法,因为它非常低效。(请参见下面的timeit结果。)如果不能使用包装函数,或许在Py2时代这是必需的,但那些日子现在已经过去了。 - Gringo Suave
这个不行。我在合并的字典中得到了键作为值。 - mLstudent33

750

另一个选择:

z = x.copy()
z.update(y)

106
为了澄清为什么这不符合问题提供的标准: 它不是单个表达式,也没有返回 z。 - Alexander Oh
22
这样说吧:如果你需要在交付代码给其他人时添加两行注释来解释你的一行代码……那么你真的只用了一行吗? :) 我完全同意Python不擅长这个:应该有更简单的方法。虽然这个答案更加Python风格,但它是否真的那么明确或清晰呢?“更新”并不是人们经常使用的“核心”功能之一。 - eric
15
好的,如果人们坚持把它写成一行代码,你可以使用(lambda z: z.update(y) or z)(x.copy()) :P - towr
@AlexanderOh 我不确定这是一个笑话还是真的;至少从它的工作效果来看,我认为这是一个完全有效的答案!当然,第二条评论确立了一个先例!无论如何,这确实是符合 Python 风格的! - William Martens
7
@WilliamMartens这不是个玩笑。但是,让我们面对现实吧,如果你优化单行表达式,那么你就在为错误的事情进行优化。 - Alexander Oh
1
@AlexanderOh 是的,我同意!祝你一切顺利! - William Martens

441

另一种更简洁的选择:

z = dict(x, **y)

注意:虽然这个方法已经变得很流行,但需要指出的是,如果 y 具有任何非字符串键,那么这种方法实际上是滥用了 CPython 实现细节,它在 Python 3、PyPy、IronPython 或 Jython 中都无法工作。此外,Guido 不赞成使用此方法。因此,我不能建议在具备向前兼容性或跨平台可移植性的代码中使用此技术,这意味着应该完全避免使用此方法。


3
在Python 3、PyPy和PyPy 3中运行良好,但无法确定Jython或Iron的情况。鉴于这种模式已经明确记录(请参见此文档中的第三个构造函数形式),我认为它不是一个“实现细节”,而是有意的特性使用。 - amcgregor
19
@amcgregor,您错过了关键短语“如果y具有任何非字符串键”。这在Python3中不起作用;它在CPython 2中工作的事实是一个不能依赖的实现细节。如果您保证所有键都是字符串,那么这是一个完全支持的选项。 - Carl Meyer

258

这可能不是一个受欢迎的答案,但您几乎肯定不想这样做。如果您想要一个合并的副本,则使用copy(或deepcopy,具体取决于您的需求)然后进行更新。这两行代码更易读 - 更符合Pythonic风格 - 比单行创建带有.items() + .items()的代码更容易理解。显式优于隐式。

此外,当您使用.items()(在Python 3.0之前)时,您正在创建一个包含字典项的新列表。如果您的字典很大,那么这将是相当多的开销(两个大列表将立即被丢弃,一旦合并的字典被创建)。update()可以更有效地工作,因为它可以逐个遍历第二个dict的项。

time而言:

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

我认为为了可读性,第一和第二之间的微小减速是值得的。此外,在Python 2.3中才添加了用于创建字典的关键字参数,而copy()和update()将在较旧版本中起作用。


195
在后续的回答中,您问到了这两种替代方案的相对性能:
z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

在我的电脑上(一台运行Python 2.5.2的普通x86_64),另一种“z2”不仅更短、更简单,而且速度也显著更快。您可以使用Python附带的"timeit"模块自行验证。
示例1: 将20个连续整数映射到它们本身的相同字典。
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z2赢了大约3.5倍。不同的字典似乎产生非常不同的结果,但是z2总是能够赢得比较优势。(如果您对相同的测试获得不一致的结果,请尝试传入-r,并将数字设为大于默认值3的数字。)

示例2: 将252个短字符串映射到整数以及反之亦然的非重叠字典:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

z2 的获胜大约是 z1 的十倍。在我的观点中,这算是一次相当大的胜利!

在比较了这两个之后,我想知道 z1 的表现是否受到构建两个项目列表的开销的影响,这进而让我想知道这种变化是否能够更好地发挥作用:

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

一些快速测试,例如:

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

我的结论是z3z1快一些,但远不及z2快。显然这并不值得额外的打字时间。

这次讨论还缺少一个重要内容,那就是使用“显而易见”的方法合并两个列表的性能比较:使用update方法。为了使与不修改x或y的表达式保持相等,我将复制x而不是直接在原地修改它,如下所示:

z0 = dict(x)
z0.update(y)

一个典型的结果:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

换言之,z0z2的性能似乎基本相同。您认为这可能是巧合吗?我不这么认为... 事实上,我会断言纯Python代码不可能比这更好。如果您可以在C扩展模块中做得更好,那么我想Python社区可能会有兴趣将您的代码(或您的方法变体)纳入Python核心。Python在许多地方使用dict;优化其操作非常重要。您也可以将其写成:
z0 = x.copy()
z0.update(y)

就像Tony所做的那样,但(不出所料)符号表示上的差异对性能没有任何可测量的影响。 选择看起来正确的方法即可。 当然,他绝对正确地指出两个语句版本要容易理解得多。


9
这在Python 3中不起作用;items()不可连接,而iteritems()不存在。 - Antti Haapala -- Слава Україні

191
在Python 3.0及以上版本中,您可以使用collections.ChainMap将多个字典或其他映射组合在一起,创建一个可更新的视图:
>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(ChainMap({}, y, x))
>>> for k, v in z.items():
        print(k, '-->', v)
    
a --> 1
b --> 10
c --> 11
Python 3.5 及以上版本更新: 您可以使用 PEP 448 扩展的字典打包和解包。这非常快速和简便:
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}

针对Python 3.9及更高版本的更新:您可以使用PEP 584联合运算符:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x | y
{'a': 1, 'b': 10, 'c': 11}

6
但是在使用ChainMap时应该谨慎,因为有一个陷阱,如果您有重复的键,则会使用第一个映射中的值,并且当您对ChainMap c调用del时,将删除该键的第一个映射。 - Slayer
12
你期望它还能做什么?这是链式命名空间正常的工作方式。考虑bash中$PATH的工作方式。在路径上删除一个可执行文件并不排除上游存在另一个同名的可执行文件。 - Raymond Hettinger
4
我同意,只是加了个注意。大多数人可能不知道这一点。 :D - Slayer
@Prerit 你可以将其转换为dict来避免这种情况,即:dict(ChainMap({}, y, x)) - wjandrea
2
建议编辑队列已满,但有人将@wjandrea的修改放入答案中,这是错误的 - 它不再是“一个可更新的视图”。应该撤销此编辑。 - Gloweye
@RaymondHettinger 我们都知道你以最合理的方式设计了 ChainMap,非常感谢! - run_the_race

155

我想要类似的东西,但希望能够指定如何合并重复键上的值,因此我进行了一些修改(但没有进行大量测试)。显然,这不是一个单一的表达式,但它只需要一个函数调用。

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result

当短而简单的解决方案(通过第二个字典替换常见键的值)的默认行为不被期望时,以下是一个便利的解决方案。对于Python 3,dicts中不再可用iteritems(),可以简单地使用items()代替。 - Corentor

126

递归/深度更新字典

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

演示:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

输出:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

感谢rednaw进行编辑。

2
这并没有回答问题。问题明确要求从原始字典x和y中创建一个新的字典z,其中y的值替换x的值 - 而不是更新字典。这个答案通过添加来自x的值在原地修改了y。更糟糕的是,它没有复制这些值,因此可以进一步修改修改后的字典y,并且修改可能会反映在字典x中。@Jérôme我希望这段代码不会对您的应用程序造成任何错误 - 至少考虑使用deepcopy来复制这些值。 - Russia Must Remove Putin
2
@AaronHall同意这并没有回答问题。但它满足了我的需求。我理解这些限制,但在我的情况下这不是问题。想一想,也许名称有误导性,因为它可能会引起深拷贝的联想,但它确实解决了深度嵌套的问题。这里是Martellibot的另一个实现:https://dev59.com/NnA75IYBdhLWcg3wi5wr。 - Jérôme

108

我用perfplot对所建议的进行了基准测试,发现

x | y   # Python 3.9+

是最快的解决方案,加上优秀的旧方法

{**x, **y}

temp = x.copy()
temp.update(y)

在此输入图片描述


用于复制图形的代码:

from collections import ChainMap
from itertools import chain
import perfplot


def setup(n):
    x = dict(zip(range(n), range(n)))
    y = dict(zip(range(n, 2 * n), range(n, 2 * n)))
    return x, y


def copy_update(x, y):
    temp = x.copy()
    temp.update(y)
    return temp


def add_items(x, y):
    return dict(list(x.items()) + list(y.items()))


def curly_star(x, y):
    return {**x, **y}


def chain_map(x, y):
    return dict(ChainMap({}, y, x))


def itertools_chain(x, y):
    return dict(chain(x.items(), y.items()))


def python39_concat(x, y):
    return x | y


b = perfplot.bench(
    setup=setup,
    kernels=[
        copy_update,
        add_items,
        curly_star,
        chain_map,
        itertools_chain,
        python39_concat,
    ],
    labels=[
        "copy_update",
        "dict(list(x.items()) + list(y.items()))",
        "{**x, **y}",
        "chain_map",
        "itertools.chain",
        "x | y",
    ],
    n_range=[2 ** k for k in range(18)],
    xlabel="len(x), len(y)",
    equality_check=None,
)
b.save("out.png")
b.show()

1
尝试重现结果,但出现了以下错误-> TypeError: copy_update()需要1个位置参数,但提供了2个 - Aaditya Ura
你能否添加测试条件?确切的Python版本。Python实现(例如CPython)。硬件(CPU类型和型号,L2缓存大小,时钟速度,动态时钟速度缩放,核心数量等)。操作系统(类型,版本和版本号)。任何其他相关信息(例如,温/冷)。 (但请勿包括“编辑:”,“更新:”或类似内容 - 答案应该看起来像是今天写的) - Peter Mortensen
@AadityaUra 在我的本地 Python 3.10 上,代码没有进行修改就可以正常运行。 - mlen108
最好有超过两个字典的示例,例如 x_1 | x_2 | ... | x_n - william_grisaitis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接