|
我有两个 Python 字典,我想写一个单一的表达式来返回这两个字典,合并(即合并)。update()方法将是我需要的,如果它返回其结果而不是修改字典。
2 k' o4 ~6 v2 p3 y- P; V( I! `7 l3 T6 ?9 z7 r
- >>> x = {'a': 1,'b': 2}>>> y = {'b': 10,'c': 11}>>> z = x.update(y)>>> print(z)None>>> x{'a': 1,'b': 10,'c code]我怎样才能得到最终合并的字典?z,而不是x?
b( n0 {# o. X2 y# o; _6 L2 ] - (应该特别清楚最后一次胜利的冲突处理dict.update()也是我在寻找的。. G1 C2 \- W, H. q9 R5 \! t q
- 9 n0 U. l" M7 L# W1 {/ ?! {
- 解决方案: + G, L! ? u) E/ d+ Q
- 如何在单个表达式中合并两个 Python 字典?对于字典xand y,z成为浅层合并的字典,其中值y取代 值x。' P Z0 E- u) t
- + f. K7 c- x ^+ R. F
- 在Python 3.9.0或更高(释放2020年10月17日)
EP-在这里讨论、实现、提供最简单的方法: py z = x | y # NOTE: 3.9 ONLY
. K4 Z3 L' o) v, G- k - q, S* P, n% g! y0 a
- 在 Python 3.5 或更高版本:
py z = {**x,**y}% S! T' P- k3 a& m2 k8 v
- * S3 ^% @0 o, p! u' g
- 在 Python 2(或 3.在4 或更低版本中编写函数:
py 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 z2 \1 n# R" c, _! e
- 现在:
+ B/ `% Y _: Q5 I. y' v' V - py z = merge_two_dicts(x,y)! g4 q7 d& s8 Y& {& t1 b( D( O
- 解释假设你有两个字典,你想在不改变原始字典的情况下将它们合并到一个新字典中:" B8 T6 s# X. h: P3 E' N# {
- [code]x = {'a': 1,'b': 2}y = {'b': 3,'ccode]期待的结果是得到一个新的字典 ( z),第二值,第二字典值覆盖第一字典值。# ~+ p6 }7 `6 N( U
- [code]>>> z{'a': 1,'b': 3,'ccode]在PEP 448 Python 3.在5 中使用的新语法是[code]z = {**x,**y}' _5 X/ u1 [: k' K2 H& ]4 {5 N
它确实是一种单一的表达。
5 b8 B0 b6 G5 R7 g4 B请注意,我们也可以与文本符号合并:
% V6 N9 ^% e9 B4 t% g- j1 R0 Fz = {**x,'foo': 1,'bar': 2,**y}
1 I, q( ]1 T. P0 b, k" Z 现在:9 b* v- P; E7 V* A! j' Z$ [5 I
' R6 C( Q% n' X9 q% ^
- >>> z{'a': 1,'b': 3,'foo': 1,'bar': 2,'ccode]它现在显示为 3.5 PEP 478的发布时间表已经实现,现在已经进入Python 3.在5 新功能文档中。" |' ~3 L7 G2 Z9 H+ G0 ^6 W
- 然而,许多组织仍在使用 Python 2,您可能希望以后以兼容的方式执行此操作。Python 2 和 Python 3.0-3.4 中可用的经典 Pythonic 方法是将其作为两个过程执行:[code]z = x.copy()z.update(y) # which returns None since it mutates z8 {3 R6 V( m! e$ i7 G* w" q
在这两种方法中,y排名第二,其值将被替换x的值,因此b我们的最终结果将指向3。
+ n+ K/ g8 G# u! Q) [- q) ^+ g% c还没有在 Python 3.5 上,但我想要一个单一的表达式假如你还没用 Python 3.5 或者需要编写后兼容的代码,你想要在单个表达式中使用,那么性能最好的方法是将其放入函数中:
0 i/ J& H5 r/ E( F& p3 l7 x9 Xdef 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
+ ^- R4 {! Y8 D1 } 然后你有一个表达式:
3 z3 R$ o: V* O1 }z = merge_two_dicts(x,y)
3 ` P$ A `% g 您还可以创建一个函数来合并任何数量的字典,从零到非常大的数字:
4 j" |/ S. {( v2 [6 @0 E3 xdef 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 result8 F6 s9 `4 h- k2 C1 N) T
该函数适用于所有字典 Python 2 和 3a到g:
( u9 z. u; S: z) xz = merge_dicts(a,b,c,d,e,f,g)
$ _+ F0 O. ?/ h* [5 C$ q 和键值对g优先于字典ato f,依此类推。
5 B7 E K( C+ ]7 |3 J' l批评其他答案不要使用你在以前的答案中看到的内容:
2 t9 K) A& N3 Z. g4 N2 nz = dict(x.items() y.items())
|* S7 s. c) o 在 Python 2 在内存中,你是每个 dict 创建两个列表,在内存中创建第三个列表,其长度等于前两个列表的长度,然后丢弃所有三个列表创建 dict。在 Python 3 中,这将失败,因为你要两个dict_items将对象添加在一起,而不是两个列表 -) F" |# ]+ U& M/ Z' E4 k, a: u
>>> 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'+ B+ G5 o# i! d$ D$ A
例如,您必须创建它们的显式列表z = dict(list(x.items() list(y.items())). 这是浪费资源和计算能力。
0 B$ M6 w1 J4 a类似地,当值是不可散列的对象(如列表),items()在 Python 3(viewitems()在 Python 2.7 中)并集也会失败。即使你的值可以散列,因为集合在语义上是无序的,所以行为在优先级上是不定义的。所以不要这样做:
! Q& f, y0 n3 A4 E1 ^$ r% h>>> c = dict(a.items() | b.items())
~ H* [7 [: `/ m+ X 这个例子展示了当值不能散列时会发生什么:
* F% p9 U B& U- ^>>> x = {'a>>> y = {'b>>> dict(x.items() | y.items())Traceback (most recent call last): File "",line 1,in TypeError: unhashable type: 'list'
P. y) t1 p" c0 G" N 这是一个y应该有优先示例,但是x 由于集合的任意顺序而保留from 的值:5 t5 G+ `( h% x
l2 S' x$ Y& C. z: Y- >>> x = {'a': 2}>>> y = {'a': 1}>>> dict(x.items() | y.items()){'a code]另一个你不应该用的黑客:[code]z = dict(x,**y)
! `, c" e' e5 ^# W5 z; t: Z 这使用dict构造函数非常快,内存效率高(甚至比我们的两个步骤多一点),但除非你确切知道这里发生了什么(也就是说,第二个 dict 作为关键字参数传递给 dict 构造函数),很难阅读,不是预期用法,所以不是 Pythonic。
; ^ D# c$ L6 g2 w3 B这是在 django修复用法示例。
0 i$ @2 a* X) o, x; `字典旨在使用可散列键(例如frozensets 或元组),但是当键不是字符串时,此方法在 Python 3 失败。; B N |. H' a
>>> c = dict(a,**b)Traceback (most recent call last): File "",line 1,in TypeError: keyword arguments must be strings
: e z9 S$ k7 g' H" y; m9 Q7 P 该语言的创造者 Guido van Rossum 写道:
7 V9 S# l8 P* u我可以声明 dict({},{1:3}) 是非法的,因为毕竟是对的 滥用机制。2 M) i9 I! s$ m- L5 Z0 s* l* G9 U
和) I/ U+ W- n8 \ O; C8 C
显然 dict(x,**y) 作为 call x.update(y) and return x”的“cool hack四处走动。就我个人而言,我认为它比酷更卑鄙。
. j5 n! p4 U7 q" |5 g" g$ Y根据我的理解(以及语言创造者的理解),预期使用dict(**y)以可读性为目的创建字典,例如:3 U* x- h& ?* t, L* C& \, {& H
dict(a=1,b=10,c=11)5 K7 b* ~! u5 J6 {- l, F) q9 [9 q! F
代替
) w; h3 o% r: |# R1 `+ W0 o6 @& {) B! G3 e2 w2 m8 n
- {'a': 1,'b': 10,'c code]回复评论不管 Guido 怎么说,dict(x,**y)它都符合 dict 规范,顺便说一句。适用于 Python 2 和 3。事实上,这只适用于字符串键,而不是 dict 的缺点。在这个地方使用 运算符不是滥用机制,事实上, 是为了传递字典作为关键字而设计的。9 `+ h2 _5 J1 `
- 同样,当键不是字符串时,它也不适用于 3。隐式调用合同是命名空间使用普通字典,用户只能传输字符串形式的关键字参数。所有其他可调用对象都被迫执行。dict在 Python 2 打破了这个致性:[code]>>> 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}
5 z F9 K1 U" a: T# s# N; e 考虑到 Python 其他实现(PyPy、Jython、IronPython),这种不一致是很糟糕的。因此它在 Python 3 已经修复,因为这种用法可能是一个突破性的变化。
+ w7 w# N4 q+ k2 H我告诉你,故意编写只适用于语言版本或某些任意约束的代码是恶意的。% Z" y$ C% Y5 I% O: ~
更多评论:5 z! r- y! O/ G& n2 n
dict(x.items() y.items() 还是 Python 2 中最可读的解决方案。可读性很重要。
# f4 w+ x7 q( ?% o我的回答:merge_two_dicts(x,y)事实上,如果我们真的关心可读性,对我来说似乎更清楚。而且它不向前兼容,因为 Python 2 越来越被弃用。/ e9 M1 B4 O" X, |
{**x,**y}嵌套字典似乎没有处理。嵌套键的内容只是被覆盖,而不是合并 […] 我最终被这些答案所困扰,我很惊讶没有人提到它。在我对合并一词的解释中,这些答案描述了用另一个字典更新一个字典,而不是合并。
& v' |: n, f, F! y是的,我必须让你回到这个问题,它要求正确两个字典进行浅层合并,第一个值被第二个值覆盖 - 在单个表达式中。
' m% K1 _4 z8 O# N- H5 R' I! ?假设有两个字典,一个可能会递归地将它们合并到一个函数中,但是您应该注意不要修改来自任一来源的字典,避免这种情况的最可靠方法是在赋值时进行复制。由于键必须是可散列的,因此通常是不可变的,复制它们是没有意义的:/ W9 u4 G1 v0 L' K* a B
from copy import deepcopydef 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
# a n K6 i! q, S, _9 ]- G 用法:
}1 V0 }0 ]1 I/ j( R9 q
4 V7 H a2 T2 z5 U* f, |- >>> x = {'a{1:{}b >>> y = {'b{c >>> dict_of_dicts_merge(x,y){'b ac code]其他类型的事故远远超出了这个问题。) J2 S! n/ }. j, T" L2 ~3 O
- 性能差但正确Ad-hoc这些方法的性能较低,但它们会提供正确的行为。少得多比高性能copy和update或新的拆包,因为他们通过在更高的抽象水平的每个键-但是他们做的尊重优先顺序(后者字典优先)0 H! y" j* x- L; q; i8 K: S/ _
- 您还可以在字典理解中手动链接字典:[code]{k: v for d in dicts for k,v in d.items()} # iteritems in Python 2.72 ^" t# s5 G! g% c9 o! _' F
或者在 Python 2.也许早在 2.4 引入生成器表达式时:+ `( B2 D. i9 L: V% t c
dict((k,v) for d in dicts for k,v in d.items()) # iteritems in Python 2- o$ f" E0 I2 y# k5 }
itertools.chain 将迭代器以正确的顺序链接到键确:
4 q. M; ~% @2 b8 Q' Yfrom itertools import chainz = dict(chain(x.items(),y.items())) # iteritems in Python 2$ ~; p8 s. a) n* {: J( s
性能分析我只会分析已知行为的正确用法。(自包含,可以自己复制粘贴。4 F2 X) X: g8 g! _- R9 ~
: R$ P u3 D! s0 ~- from timeit import repeatfrom itertools import chainx = 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())code]在 Python 3.8.1 中,NixOS:[code]>>> 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 -aLinux nixos 4.19.113 #1-NixOS SMP Wed Mar 25 07:06:15 UTC 2020 x86_64 GNU/Linux
% H* a$ R% f3 `2 s5 z
|
|