我有两个Python字典,我想编写一个返回这两个字典的表达式,合并 . update()
方法将是我需要的,如果它返回其结果而不是就地修改dict .
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}
如何在 z
中获得最终合并的字典,而不是 x
?
(要清楚的是, dict.update()
的最后一次冲突处理也是我正在寻找的 . )
30 回答
对于在两个词典中都带有键的项目('b'),您可以通过将最后一个放在最后,控制哪一个最终出现在输出中 .
Two dictionaries
n dictionaries
sum
表现不佳 . 见https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/我想要类似的东西,但是能够指定复制键上的值是如何合并的,所以我将其解决了(但没有对它进行大量测试) . 显然这不是单个表达式,而是单个函数调用 .
我没有使用副本时可以想到的最佳版本是:
它比
dict(x.items() + y.items())
快,但没有n = copy(a); n.update(b)
快,至少在CPython上 . 如果您将iteritems()
更改为items()
,此版本也适用于Python 3,这是由2to3工具自动完成的 .就个人而言,我最喜欢这个版本,因为它在单一功能语法中描述了我想要的东西 . 唯一的小问题是,从y的值优先于x的值,并没有完全明显,但我不认为很难弄明白 .
对于字典
x
和y
,z
成为浅层合并的字典,其中y
的值替换了x
中的值 .现在:
解释
假设您有两个dicts,并且您希望将它们合并到一个新的dict而不更改原始的dicts:
期望的结果是获得一个新的字典(
z
),其值合并,第二个字典的值覆盖第一个 .在PEP 448和available as of Python 3.5中提出的新语法是
它确实是一个表达式 .
请注意,我们也可以使用文字符号合并:
现在:
它现在显示为在release schedule for 3.5, PEP 478中实现,现在它已经进入What's New in Python 3.5文档 .
但是,由于许多组织仍在使用Python 2,因此您可能希望以向后兼容的方式执行此操作 . Python 2和Python 3.0-3.4中提供的经典Pythonic方法是通过两个步骤完成的:
在这两种方法中,
y
将成为第二个,其值将替换x
的值,因此'b'
将在我们的最终结果中指向3
.尚未在Python 3.5上,但想要一个表达式
如果你还没有使用Python 3.5,或者需要编写向后兼容的代码,并且你想在单个表达式中使用它,那么最正确的方法就是将它放在一个函数中:
然后你有一个表达式:
您还可以创建一个函数来合并未定义数量的dicts,从零到非常大的数字:
对于所有dicts,此函数将在Python 2和3中使用 . 例如给出dicts
a
到g
:g
中的键值对将优先于dictsa
至f
,依此类推 .其他答案的批评
不要使用您在之前接受的答案中看到的内容:
在Python 2中,您在内存中为每个dict创建两个列表,在内存中创建第三个列表,其长度等于放在一起的前两个列表的长度,然后丢弃所有三个列表以创建dict . In Python 3, this will fail 因为您要将两个
dict_items
对象一起添加,而不是两个列表 -你必须明确地将它们创建为列表,例如
z = dict(list(x.items()) + list(y.items()))
. 这是浪费资源和计算能力 .类似地,当值是不可用的对象(例如列表)时,在Python 3中使用
items()
的并集(Python 2.7中的viewitems()
)也将失败 . 即使您的值是可以清除的, since sets are semantically unordered, the behavior is undefined in regards to precedence. So don't do this:此示例演示了值不可用时会发生什么:
这是y应该具有优先权的示例,但是由于任意顺序的集合而保留x中的值:
另一个黑客你不应该使用:
这使用
dict
构造函数,并且非常快且内存效率高(甚至比我们的两步过程稍微多一些),但除非你确切地知道这里发生了什么(也就是说,第二个dict作为关键字参数传递给dict构造函数),它's difficult to read, it'不是预期的用法,所以它不是Pythonic .以下是remediated in django的用法示例 .
Dicts旨在采用可清洗密钥(例如frozensets或元组),但 this method fails in Python 3 when keys are not strings.
来自mailing list,该语言的创造者Guido van Rossum写道:
和
我的理解(以及对creator of the language的理解)
dict(**y)
的预期用途是为了可读性目的而创建dicts,例如:代替
对评论的回复
同样,当键是非字符串时,它不适用于3 . 隐式调用 Contract 是命名空间采用普通的dicts,而用户只能传递字符串的关键字参数 . 所有其他callables强制执行它 .
dict
在Python 2中打破了这种一致性:鉴于Python的其他实现(Pypy,Jython,IronPython),这种不一致性很糟糕 . 因此它在Python 3中得到了修复,因为这种用法可能是一个突破性的变化 .
我向你提出,故意编写只能在一种语言版本中工作的代码或仅在某些任意约束条件下工作的代码是恶意无能的 .
更多评论:
我的回答:
merge_two_dicts(x, y)
对我来说实际上似乎更清楚,如果我们真的关心可读性 . 并且它不向前兼容,因为Python 2越来越被弃用 .是 . 我必须回过头来回答一个问题,即在一个表达式中要求 two 词典与第一个's values being overwritten by the second'的浅层合并 .
假设有两个词典字典,一个可以递归地将它们合并到一个函数中,但是你应该注意不要从任何一个源修改dicts,并且最可靠的方法是在分配值时复制它们 . 由于密钥必须是可清洗的,因此通常是不可变的,因此复制它们是没有意义的:
用法:
提出其他 Value 类型的意外事件远远超出了这个问题的范围,所以我将在my answer to the canonical question on a "Dictionaries of dictionaries merge"指出 .
性能较差但正确的Ad-hoc
这些方法性能较差,但它们将提供正确的行为 . 它们的性能要比
copy
和update
或新解包的性能低得多,因为它们在更高的抽象层次上遍历每个键值对,但它们确实尊重优先顺序(后面的序列优先)你也可以在dict理解中手动链接dicts:
或者在python 2.6中(当引入生成器表达式时可能早在2.4):
itertools.chain
将以正确的顺序将迭代器链接到键值对:性能分析
我只会对已知行为正确的用法进行性能分析 .
以下是在Ubuntu 14.04上完成的
在Python 2.7(系统Python)中:
在Python 3.5(deadsnakes PPA)中:
字典资源
My explanation of Python's dictionary implementation, updated for 3.6.
Answer on how to add new keys to a dictionary
Mapping two lists into a dictionary
官方Python docs on dictionaries
The Dictionary Even Mightier - 由Brandon Rhodes在Pycon 2017上发表演讲
Modern Python Dictionaries, A Confluence of Great Ideas - Raymond Hettinger在Pycon 2017上的演讲
Python 3.5(PEP 448)允许更好的语法选项:
甚至
替代:
另一个更简洁的选择:
Note :这已经成为一个流行的答案,但重要的是要指出,如果
y
有任何非字符串键,这一点的工作原理是滥用CPython实现细节,它在Python 3中不起作用,或者在PyPy,IronPython或Jython中 . 另外,Guido is not a fan . 所以我不推荐这种技术用于前向兼容或交叉实现的可移植代码,这实际上意味着它应该完全避免 .这可以通过单个字典理解来完成:
在我看来,“单一表达”部分的最佳答案是不需要额外的功能,而且很短 .
滥用导致Matthew's answer的单表达式解决方案:
你说你想要一个表达式,所以我滥用
lambda
来绑定一个name和tuples来覆盖lambda的one-expression限制 . 随意畏缩 .如果您不关心复制它,您当然也可以这样做:
尽管这个浅层词典的答案很好,但这里定义的方法实际上并没有进行深层词典合并 .
示例如下:
人们会期待这样的结果:
相反,我们得到这个:
如果它真的是一个合并,那么'one'条目应该有'depth_2'和'extra'作为其字典中的项目 .
使用链也不起作用:
结果是:
rcwesick给出的深度合并也会产生相同的结果 .
是的,它可以合并样本字典,但它们都不是合并的通用机制 . 一旦我编写了一个执行真正合并的方法,我将在稍后更新 .
是pythonic . 使用comprehension:
在您的情况下,您可以做的是:
这将根据您的需要将最终的字典放在
z
中,并使键b
的值被第二个(y
)dict的值正确覆盖:如果你使用Python 3,它只是稍微复杂一点 . 要创建
z
:在Python 3中,您可以使用collections.ChainMap将多个dicts或其他映射组合在一起以创建单个可更新视图:
(仅适用于Python2.7 *; Python3 *有更简单的解决方案 . )
如果您不反对导入标准库模块,则可以这样做
(
lambda
中的or a
位是必需的,因为dict.update
成功时总是返回None
. )这可能赢得了't be a popular answer, but you almost certainly do not want to do this. If you want a copy that'的合并,然后使用副本(或deepcopy,取决于你想要的),然后更新 . 这两行代码比使用.items().items()的单行创建更具可读性 - 更多Pythonic . 显式优于隐式 .
此外,当您使用.items()(Python 3.0之前)时,您正在创建一个包含dict项目的新列表 . 如果你的词典很大,那么开销很大(两个大型列表一旦创建合并的dict就会被丢弃) . update()可以更有效地工作,因为它可以逐项运行第二个dict .
就time而言:
IMO前两者之间的微小减速对于可读性是值得的 . 此外,字典创建的关键字参数仅在Python 2.3中添加,而copy()和update()将在旧版本中使用 .
虽然问题已经多次回答,但这个问题的简单解决方案尚未列出 .
它与z0和上面提到的邪恶z2一样快,但易于理解和改变 .
如果你认为lambdas是邪恶的,那就不要再读了 . 根据要求,您可以使用一个表达式编写快速且内存有效的解决方案:
如上所述,使用两行或编写函数可能是更好的方法 .
在Python 3.5中,您可以使用unpack
**
来创建新字典 . 这种方法在过去的答案中没有显示出来 . 此外,最好使用{}
而不是dict()
. 因为{}
是一个python文字而dict()
涉及一个函数调用 .在python3中,
items
方法no longer returns a list,而是一个视图,它就像一个集合 . 在这种情况下,你需要采用set union,因为与+
的连接将不起作用:对于2.7版中类似python3的行为,
viewitems
方法应该代替items
:不管怎样我更喜欢这种符号,因为将它看作是一个联合操作而不是连接似乎更自然(如 Headers 所示) .
Edit:
python 3还有几点 . 首先,请注意
dict(x, **y)
技巧在python 3中不起作用,除非y
中的键是字符串 .此外,Raymond Hettinger的Chainmap answer非常优雅,因为它可以将任意数量的dicts作为参数,但是from the docs看起来它依次查看每个查找的所有dicts的列表:
如果您的应用程序中有大量查找,这会降低您的速度:
所以查找速度要慢一个数量级 . 我是Chainmap的粉丝,但在可能有很多查找的地方看起来不那么实用 .
在python 3中:
日期:
文件:https://docs.python.org/3/library/collections.html#collections.ChainMap:
使用保留顺序的itertools的简单解决方案(后面的dicts优先)
它的用法是:
我在今天列出的解决方案中遇到的问题是,在合并的字典中,键“b”的值是10,但是,根据我的想法,它应该是12.在这一点上,我提出以下内容:
结果:
递归/深度更新dict
示范:
输出:
谢谢rednaw的编辑 .
在后续回答中,您询问了这两种备选方案的相对表现:
在我的机器上,至少(相当普通的x86_64运行Python 2.5.2),替代
z2
不仅更短更简单,而且速度更快 . 您可以使用Python附带的timeit
模块自行验证 .示例1:将20个连续整数映射到自身的相同字典:
z2
胜出3.5左右 . 不同的词典似乎产生了截然不同的结果,但是_1052223似乎总是会出现 . (如果同一测试的结果不一致,请尝试使用大于默认值3的数字传入-r
. )示例2:非重叠字典将252个短字符串映射为整数,反之亦然:
z2
赢得大约10倍 . 这在我的书中是一个相当大的胜利!在比较了这两个之后,我想知道
z1
的糟糕表现是否可归因于构建两个项目列表的开销,这反过来又让我想知道这种变化是否会更好:一些快速测试,例如
让我得出结论,
z3
比z1
快一些,但不如z2
快 . 绝对不值得所有额外打字 .这个讨论仍然缺少一些重要的东西,这是将这些备选方案与合并两个列表的方法进行性能比较:使用
update
方法 . 为了尝试使表达式保持平等,没有一个表达式修改x或y,我将复制x而不是就地修改它,如下所示:一个典型的结果:
换句话说,
z0
和z2
似乎具有基本相同的性能 . 你认为这可能是巧合吗?我不....事实上,我认为纯Python代码不可能比这更好 . 如果你可以在C扩展模块中做得更好,我想Python人员可能会有兴趣将你的代码(或你的方法的变体)合并到Python核心中 . Python在很多地方使用
dict
;优化其运营是一件大事 .你也可以这样写
正如Tony所做的那样,但(并不奇怪)表示法中的差异结果表明不会对性能产生任何可测量的影响 . 使用适合您的任何一种 . 当然,他绝对正确地指出双语句版本更容易理解 .
这应该可以解决您的问题 .
在这些阴暗和可疑的答案中,这个光辉的例子是合并Python中的dicts的唯一好方法,由生活的独裁者Guido van Rossum自己赞同!其他人建议这一半,但没有把它放在一个功能 .
得到:
对于Python 2:
对于Python 3:
它给出了输出:
{'a': 1, 'c': 11, 'b': 10}
借鉴这里和其他地方的想法,我理解了一个功能:
用法(在python 3中测试):
你可以使用lambda代替 .