如何在一个表达式中合并两个字典? | 您所在的位置:网站首页 › CentOS7搭建sendmail › 如何在一个表达式中合并两个字典? |
回答问题
我想将两个字典合并到一个新字典中。 x = {'a': 1, 'b': 2} y = {'b': 3, 'c': 4} z = merge(x, y) >>> z {'a': 1, 'b': 3, 'c': 4}每当两个字典中都存在键k时,只应保留值y[k]。 Answers 如何在一个表达式中合并两个 Python 字典?对于字典x和y,它们的浅合并字典z从y获取值,替换来自x的值。 在 Python 3.9.0 或更高版本中(2020 年 10 月 17 日发布,PEP-584,在这里讨论): z u003d x |是的 在 Python 3.5 或更高版本中: z u003d {**x, **y} 在 Python 2(或 3.4 或更低版本)中编写一个函数:zoz100027` def merge_two_dicts(x, y): z u003d x.copy() # 从 x 的键和值开始 z.update(y) # 用 y 的键和值修改 z 返回 z 现在:z u003d merge_two_dicts(x, y) #### 解释 假设您有两个字典,并且希望将它们合并到一个新字典中而不更改原始字典:x = {'a': 1, 'b': 2} y = {'b': 3, 'c': 4} 期望的结果是获得一个新字典 (`z`),其中的值合并,第二个字典的值覆盖第一个字典的值。z {'a': 1, 'b': 3, 'c': 4} 从 Python 3.5 起,[PEP 448](https://www.python.org/dev/peps/pep-0448)和[中提出的新语法](https://mail.python.org/pipermail/python-dev/2015-February/138564.html)是z = {**x, **y} 它确实是一个单一的表达方式。 请注意,我们也可以与文字符号合并:z = {**x, 'foo': 1, 'bar': 2, **y} 现在:z {'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4} 它现在显示为在 3.5 的[发布计划中实现,PEP 478](https://www.python.org/dev/peps/pep-0478/#features-for-3-5),它现在已经进入[Python 3.5 中的新增功能](https://docs.python.org/dev/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations)文档。 但是,由于许多组织仍在使用 Python 2,您可能希望以向后兼容的方式执行此操作。在 Python 2 和 Python 3.0-3.4 中可用的经典 Pythonic 方法是分两步执行此操作:z = x.copy() z.update(y) # which returns None since it mutates z 在这两种方法中,`y`将排在第二位,它的值将替换`x`的值,因此`b`在我们的最终结果中将指向`3`。 ### 还没有在 Python 3.5 上,但想要一个_单个表达式_ 如果您还没有使用 Python 3.5 或需要编写向后兼容的代码,并且您希望将其放在 _single expression_ 中,那么最好的方法是把它放在一个函数中: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) `g`中的键值对将优先于字典`a`到`f`,依此类推。 ### 对其他答案的批评 不要使用您在以前接受的答案中看到的内容:z = dict(x.items() + y.items()) 在 Python 2 中,您在内存中为每个 dict 创建两个列表,在内存中创建长度等于前两个加在一起的长度的第三个列表,然后丢弃所有三个列表以创建 dict。 **在 Python 3 中,这将失败** 因为您将两个`dict_items`对象添加在一起,而不是两个列表 -c = dict(a.items() + b.items()) Traceback (most recent call last): File "", line 1, in 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 "", line 1, in 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](https://code.djangoproject.com/attachment/ticket/13357/django-pypy.2.diff)中修复的使用[的示例。 字典旨在采用可散列的键(例如`frozenset`s 或元组),但**当键不是字符串时,此方法在 Python 3 中失败。**c = dict(a, **b) Traceback (most recent call last): File "", line 1, in TypeError: keyword arguments must be strings 在[邮件列表](https://mail.python.org/pipermail/python-dev/2010-April/099459.html)中,该语言的创建者 Guido van Rossum 写道: > 我可以声明 dict({}, \*\*{1:3}) 非法,因为它毕竟是对 \*\* 机制的滥用。 和 > 显然 dict(x, \*\*y) 正在作为“调用 x.update(y) 并返回 x”的“酷黑客”。就个人而言,我觉得它比酷更卑鄙。 我的理解(以及[语言](https://mail.python.org/pipermail/python-dev/2010-April/099485.html)的创建者的理解)`dict(**y)`的预期用途是创建字典以提高可读性,例如:dict(a=1, b=10, c=11) 代替{'a': 1, 'b': 10, 'c': 11} ### 回复评论 > 尽管 Guido 说了什么,`dict(x, **y)`符合 dict 规范,顺便说一句。适用于 Python 2 和 3。这仅适用于字符串键这一事实是关键字参数如何工作的直接结果,而不是 dict 的缺点。在这个地方使用 \*\* 运算符也不是滥用机制,事实上,\*\* 被设计为将字典作为关键字传递。 同样,当键不是字符串时,它不适用于 3。隐式调用约定是命名空间采用普通字典,而用户必须只传递字符串形式的关键字参数。所有其他可调用对象都强制执行它。`dict`在 Python 2 中打破了这种一致性:foo(**{('a', 'b'): None}) Traceback (most recent call last): File "", line 1, in 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}`似乎不处理嵌套字典。嵌套键的内容只是被覆盖,而不是合并 \[...\] 我最终被这些不递归合并的答案烧毁了,我很惊讶没有人提到它。在我对“合并”一词的解释中,这些答案描述了“用另一个更新一个字典”,而不是合并。 是的。我必须让你回到这个问题,它要求 _**two**_ 字典的 _shallow_ 合并,第一个的值被第二个的值覆盖 - 在一个表达式中。 假设有两个字典,一个可能会递归地将它们合并到一个函数中,但是您应该注意不要从任一源修改字典,避免这种情况的最可靠方法是在分配值时进行复制。由于键必须是可散列的,因此通常是不可变的,因此复制它们是没有意义的: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: {}}} 提出其他值类型的意外情况远远超出了这个问题的范围,所以我将在[中指出我对“字典合并字典”](https://stackoverflow.com/a/24088493/541136)的规范问题的回答。 ### 性能较差但正确的 Ad-hoc 这些方法的性能较差,但它们会提供正确的行为。它们的性能将比`copy`和`update`或新解包的性能_低得多_,因为它们在更高的抽象级别上迭代每个键值对,但它们_do_尊重优先顺序(后面的字典有优先权) 您还可以在[字典理解](https://www.python.org/dev/peps/pep-0274/)中手动链接字典:{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 zmin(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 ### 字典资源 * [我对 Python 的**字典实现**的解释,针对 3.6 更新。](https://stackoverflow.com/questions/327311/how-are-pythons-built-in-dictionaries-implemented/44509302#44509302) * [关于如何向字典添加新键的答案](https://stackoverflow.com/questions/1024847/add-new-keys-to-a-dictionary/27208535#27208535) * [将两个列表映射到字典中](https://stackoverflow.com/questions/209840/map-two-lists-into-a-dictionary-in-python/33737067#33737067) * [关于字典的官方 Python 文档](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) * [The Dictionary Even Mightier](https://www.youtube.com/watch?v=66P5FMkWoVU)- Brandon Rhodes 在 Pycon 2017 上的演讲 * [现代 Python 词典,伟大思想的汇合](https://www.youtube.com/watch?v=npw4s1QTmPg)- Raymond Hettinger 在 Pycon 2017 上的演讲 |
CopyRight 2018-2019 实验室设备网 版权所有 |