|
我有两个 Python 字典,我想写一个单一的表达式来返回这两个字典,合并(即合并)。update()方法将是我需要的,如果它返回其结果而不是修改字典。
6 V1 i0 [2 t6 T
, ^; r) _0 S0 ?% z, \1 j! H% i! p- >>> 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?
8 x) f5 \ T& r: W! G4 K - (应该特别清楚最后一次胜利的冲突处理dict.update()也是我在寻找的。
7 [6 ~2 [+ }0 J! M: g& g# m - + {/ Z3 o% E- }8 J ~
- 解决方案:
9 F; { N* m) s7 G6 e8 _! B( L5 c' \ - 如何在单个表达式中合并两个 Python 字典?对于字典xand y,z成为浅层合并的字典,其中值y取代 值x。% b- ]' g& L8 p" t8 ?; r7 A" @
7 Z; U& J; j) _$ U5 H: D- 在Python 3.9.0或更高(释放2020年10月17日)EP-在这里讨论、实现、提供最简单的方法:
py z = x | y # NOTE: 3.9 ONLY
+ v, X) b& t4 r4 {( \) h) ?9 v- 8 c7 ]* U I3 D1 `7 y3 S
- 在 Python 3.5 或更高版本:
py z = {**x,**y}
6 b5 r- W. `) Z2 E Q
4 Z. }' O& l0 `- 在 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 z
p2 n5 \: U$ \* L/ M7 C- 现在:
1 v* Z" ^5 J3 q8 ]" v - py z = merge_two_dicts(x,y)
4 [+ \! R' K, C. c8 u - 解释假设你有两个字典,你想在不改变原始字典的情况下将它们合并到一个新字典中:
Q2 ~( d0 Q. ?6 r$ E4 w - [code]x = {'a': 1,'b': 2}y = {'b': 3,'ccode]期待的结果是得到一个新的字典 ( z),第二值,第二字典值覆盖第一字典值。' \% G9 V U, E2 y0 v- b6 r
- [code]>>> z{'a': 1,'b': 3,'ccode]在PEP 448 Python 3.在5 中使用的新语法是[code]z = {**x,**y}
5 |- q) X8 a' ?# F 它确实是一种单一的表达。! y" N) H1 T J, l6 V5 A c# ~' L! S
请注意,我们也可以与文本符号合并:
3 _3 X. x5 n4 lz = {**x,'foo': 1,'bar': 2,**y}
( A- m/ N& V3 ]8 \0 H7 A 现在:. W) U2 C" v2 U) {( I- J
0 a( P; M1 y) M) {: B- >>> z{'a': 1,'b': 3,'foo': 1,'bar': 2,'ccode]它现在显示为 3.5 PEP 478的发布时间表已经实现,现在已经进入Python 3.在5 新功能文档中。
# O; f F G) l" G7 L - 然而,许多组织仍在使用 Python 2,您可能希望以后以兼容的方式执行此操作。Python 2 和 Python 3.0-3.4 中可用的经典 Pythonic 方法是将其作为两个过程执行:[code]z = x.copy()z.update(y) # which returns None since it mutates z3 g4 d7 G) Z5 l C* a3 L
在这两种方法中,y排名第二,其值将被替换x的值,因此b我们的最终结果将指向3。
: Z# F* P, M6 I. v s还没有在 Python 3.5 上,但我想要一个单一的表达式假如你还没用 Python 3.5 或者需要编写后兼容的代码,你想要在单个表达式中使用,那么性能最好的方法是将其放入函数中:
! x1 m3 _* u, s8 k6 N3 g- ]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
, ?6 ?. l& p: R6 Y 然后你有一个表达式:
9 l, b# B n- c, _8 m" d! rz = merge_two_dicts(x,y)% l' s. ^& E* @3 @ n* e7 }
您还可以创建一个函数来合并任何数量的字典,从零到非常大的数字:
! `. c+ |( b% A. H( Kdef 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# X7 T1 W0 W; Y. I% C$ l
该函数适用于所有字典 Python 2 和 3a到g:
: l- _; @! I5 @$ @: i- n5 xz = merge_dicts(a,b,c,d,e,f,g)
! k; h/ o+ u& V 和键值对g优先于字典ato f,依此类推。# r' {* e% V( V# z' S
批评其他答案不要使用你在以前的答案中看到的内容:
+ }2 i+ Q. q% L1 H& n, w2 Nz = dict(x.items() y.items())
8 a6 S8 H/ b. R' N6 @3 d4 I% f 在 Python 2 在内存中,你是每个 dict 创建两个列表,在内存中创建第三个列表,其长度等于前两个列表的长度,然后丢弃所有三个列表创建 dict。在 Python 3 中,这将失败,因为你要两个dict_items将对象添加在一起,而不是两个列表 -1 M; u) g) g; T
>>> 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'
* h4 M' a) ^/ c7 a% q8 i+ K 例如,您必须创建它们的显式列表z = dict(list(x.items() list(y.items())). 这是浪费资源和计算能力。
) b, B1 y3 j9 |) J+ S$ [类似地,当值是不可散列的对象(如列表),items()在 Python 3(viewitems()在 Python 2.7 中)并集也会失败。即使你的值可以散列,因为集合在语义上是无序的,所以行为在优先级上是不定义的。所以不要这样做:' I& o: }) p& y1 l4 l4 G) h
>>> c = dict(a.items() | b.items())4 {/ P+ s& P1 Z& o6 I, ?
这个例子展示了当值不能散列时会发生什么:" P7 g: f1 a: B/ S1 ]
>>> x = {'a>>> y = {'b>>> dict(x.items() | y.items())Traceback (most recent call last): File "",line 1,in TypeError: unhashable type: 'list'
) I3 B! [* \3 p3 f3 H2 F: ^ 这是一个y应该有优先示例,但是x 由于集合的任意顺序而保留from 的值:
6 U$ ]0 p3 ]( O8 y1 l/ {( F u
, p8 ~( b% i: C- >>> x = {'a': 2}>>> y = {'a': 1}>>> dict(x.items() | y.items()){'a code]另一个你不应该用的黑客:[code]z = dict(x,**y)9 r7 v0 u$ {9 A! n
这使用dict构造函数非常快,内存效率高(甚至比我们的两个步骤多一点),但除非你确切知道这里发生了什么(也就是说,第二个 dict 作为关键字参数传递给 dict 构造函数),很难阅读,不是预期用法,所以不是 Pythonic。
2 Y/ S; H+ g2 B% f% H" D4 V$ i这是在 django修复用法示例。
& `& {. _/ p2 ]/ f5 h- w字典旨在使用可散列键(例如frozensets 或元组),但是当键不是字符串时,此方法在 Python 3 失败。
& ]1 T. G+ s6 U/ d$ p' h9 d>>> c = dict(a,**b)Traceback (most recent call last): File "",line 1,in TypeError: keyword arguments must be strings
) i7 E! M7 C1 a$ D# ~ 该语言的创造者 Guido van Rossum 写道:
; `! P- @5 [$ K9 m我可以声明 dict({},{1:3}) 是非法的,因为毕竟是对的 滥用机制。
: Y* _: m; z; u0 t和4 A2 v# Y3 n2 B$ s" @
显然 dict(x,**y) 作为 call x.update(y) and return x”的“cool hack四处走动。就我个人而言,我认为它比酷更卑鄙。
1 X* y/ ?3 S0 \根据我的理解(以及语言创造者的理解),预期使用dict(**y)以可读性为目的创建字典,例如:6 L( d3 O* c' ~
dict(a=1,b=10,c=11)5 j; r! q( B4 X/ q8 y" R: G l
代替
* v4 q5 e! ?0 X7 J
, M8 G7 D/ V; B0 x- {'a': 1,'b': 10,'c code]回复评论不管 Guido 怎么说,dict(x,**y)它都符合 dict 规范,顺便说一句。适用于 Python 2 和 3。事实上,这只适用于字符串键,而不是 dict 的缺点。在这个地方使用 运算符不是滥用机制,事实上, 是为了传递字典作为关键字而设计的。, V2 K2 h9 v' k, m9 p
- 同样,当键不是字符串时,它也不适用于 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}, V4 K5 U- I8 N) ^( A$ s
考虑到 Python 其他实现(PyPy、Jython、IronPython),这种不一致是很糟糕的。因此它在 Python 3 已经修复,因为这种用法可能是一个突破性的变化。: k$ w* j; n2 J
我告诉你,故意编写只适用于语言版本或某些任意约束的代码是恶意的。6 {8 x C I6 s2 Z4 F" x; A
更多评论:
1 C9 R% l* h* ]dict(x.items() y.items() 还是 Python 2 中最可读的解决方案。可读性很重要。* \% g T- M! y: p$ f1 q
我的回答:merge_two_dicts(x,y)事实上,如果我们真的关心可读性,对我来说似乎更清楚。而且它不向前兼容,因为 Python 2 越来越被弃用。- ?5 w2 _8 X( M5 u' _
{**x,**y}嵌套字典似乎没有处理。嵌套键的内容只是被覆盖,而不是合并 […] 我最终被这些答案所困扰,我很惊讶没有人提到它。在我对合并一词的解释中,这些答案描述了用另一个字典更新一个字典,而不是合并。
9 |5 e3 c5 D' ?! G6 I是的,我必须让你回到这个问题,它要求正确两个字典进行浅层合并,第一个值被第二个值覆盖 - 在单个表达式中。
2 f5 X* C4 q0 z7 h1 I l假设有两个字典,一个可能会递归地将它们合并到一个函数中,但是您应该注意不要修改来自任一来源的字典,避免这种情况的最可靠方法是在赋值时进行复制。由于键必须是可散列的,因此通常是不可变的,复制它们是没有意义的:
! V$ ?' v- N/ i7 H3 I: R6 Kfrom 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! h9 Q* Z8 h% G/ U6 T3 l
用法:' ~% o/ p6 S" s) G( k; _
v: u; y2 i5 H$ A; I9 E' o4 _& a
- >>> x = {'a{1:{}b >>> y = {'b{c >>> dict_of_dicts_merge(x,y){'b ac code]其他类型的事故远远超出了这个问题。* l, j. m( I% C8 O
- 性能差但正确Ad-hoc这些方法的性能较低,但它们会提供正确的行为。少得多比高性能copy和update或新的拆包,因为他们通过在更高的抽象水平的每个键-但是他们做的尊重优先顺序(后者字典优先)
( B& S) Q. ~% W7 n: T8 A [ - 您还可以在字典理解中手动链接字典:[code]{k: v for d in dicts for k,v in d.items()} # iteritems in Python 2.7
9 b! ~2 c. D L; A: _: j 或者在 Python 2.也许早在 2.4 引入生成器表达式时:
/ d4 z) s* P; t+ }; wdict((k,v) for d in dicts for k,v in d.items()) # iteritems in Python 2
6 f& A/ n: L' N( @( J itertools.chain 将迭代器以正确的顺序链接到键确:
* I- J$ T6 X* Ofrom itertools import chainz = dict(chain(x.items(),y.items())) # iteritems in Python 2
6 F! o3 _+ E4 w2 O4 S3 ^9 R q f 性能分析我只会分析已知行为的正确用法。(自包含,可以自己复制粘贴。( K) J8 R2 d/ G: |! ]$ q, r
! W% B, u- d; [) T( C
- 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% p# X: j1 K! g& E3 M8 G! g! {0 g
|
|