如何在单个表达式中合并两个词典?

问题描述 投票:4021回答:40

我有两个Python字典,我想编写一个返回这两个字典的表达式,合并。 update()方法将是我需要的,如果它返回其结果而不是就地修改字典。

>>> 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()的最后一次冲突处理也是我正在寻找的。)

python dictionary merge
40个回答
4547
投票

如何在单个表达式中合并两个Python词典?

对于字典xyz成为一个浅层合并的字典,来自y的值取代了x

  • 在Python 3.5或更高版本中: z = {**x, **y}
  • 在Python 2中,(或3.4或更低版本)编写一个函数: def merge_two_dicts(x, y): z = x.copy() # start with x's keys and values z.update(y) # modifies z with y's keys and values & returns None return z 现在: z = merge_two_dicts(x, y)

Explanation

假设您有两个dicts,并且您希望将它们合并到一个新的dict而不更改原始的dicts:

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

期望的结果是获得一个新的字典(z)与值合并,第二个字典的值覆盖第一个。

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

PEP 448available as of Python 3.5中提出的新语法是

z = {**x, **y}

它确实是一个表达式。

请注意,我们也可以使用文字符号合并:

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

现在:

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

它现在显示为在release schedule for 3.5, PEP 478中实现,现在它已经进入What's New in Python 3.5文档。

但是,由于许多组织仍在使用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,或者需要编写向后兼容的代码,并且您希望在单个表达式中使用它,那么最正确的方法是将其放在函数中:

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

然后你有一个表达式:

z = merge_two_dicts(x, y)

您还可以创建一个函数来合并未定义数量的dicts,从零到非常大的数字:

def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

对于所有dicts,此函数将在Python 2和3中使用。例如鉴于ag

z = merge_dicts(a, b, c, d, e, f, g) 

g中的键值对将优先于qtsxswpoi和a,等等。

批评其他答案

不要使用你在以前接受的答案中看到的内容:

f

在Python 2中,您在内存中为每个dict创建两个列表,在内存中创建第三个列表,其长度等于放在一起的前两个列表的长度,然后丢弃所有三个列表以创建dict。在Python 3中,这将失败,因为您将两个z = dict(x.items() + y.items()) 对象添加到一起,而不是两个列表 -

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' 。这是浪费资源和计算能力。

类似地,当值是不可用的对象(例如列表)时,在Python 3(Python 2.7中的z = dict(list(x.items()) + list(y.items())))中使用items()的并集也会失败。即使您的值是可清除的,因为集合在语义上是无序的,所以行为在优先级方面是未定义的。所以不要这样做:

viewitems()

此示例演示了值不可用时会发生什么:

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

这是y应该具有优先权的示例,但是由于任意顺序的集合而保留x中的值:

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

另一个黑客你不应该使用:

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

这使用z = dict(x, **y) 构造函数,并且非常快且内存效率高(甚至比我们的两步过程稍微多一点)但除非你确切地知道这里发生了什么(也就是说,第二个dict作为关键字参数传递给dict构造函数),它很难读,它不是预期的用法,所以它不是Pythonic。

以下是使用dict的示例。

Dicts旨在获取可散列密钥(例如frozensets或元组),但是当密钥不是字符串时,此方法在Python 3中失败。

remediated in django

来自>>> 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”的“酷黑客”。就个人而言,我发现它比酷酷更卑鄙。

我的理解(以及对mailing list的理解)creator of the language的预期用途是为了可读性目的而创建dicts,例如:

dict(**y)

代替

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

对评论的回应

尽管Guido说,{'a': 1, 'b': 10, 'c': 11} 符合dict规范,顺便说一下。适用于Python 2和3.事实上,这仅适用于字符串键,这是关键字参数如何工作而不是dict短路的直接结果。在这个地方也没有使用**运算符滥用该机制,事实上**被精确地设计为将dicts作为关键字传递。

同样,当键是非字符串时,它不适用于3。隐式调用契约是命名空间采用普通的dicts,而用户只能传递字符串的关键字参数。所有其他callables强制执行它。 dict(x, **y)在Python 2中打破了这种一致性:

dict

鉴于Python的其他实现(Pypy,Jython,IronPython),这种不一致性很糟糕。因此它在Python 3中得到了修复,因为这种用法可能是一个突破性的变化。

我向你提出,故意编写只能在一种语言版本中工作的代码或仅在某些任意约束条件下工作的代码是恶意无能的。

更多评论:

>>> 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 2最易读的解决方案。可读性很重要。

我的回答:如果我们真的关心可读性,那么dict(x.items() + y.items())实际上对我来说更加清晰。并且它不向前兼容,因为Python 2越来越被弃用。

merge_two_dicts(x, y)似乎没有处理嵌套字典。嵌套键的内容被简单地覆盖,没有合并[...]我最终被这些没有递归合并的答案所灼烧,我很惊讶没有人提到它。在我对“合并”一词的解释中,这些答案描述了“用另一个更新一个字典”,而不是合并。

是。我必须回过头来回答这个问题,即要求两个词典的浅层合并,第一个的值被第二个词覆盖 - 在一个表达式中。

假设有两个词典字典,一个可以在一个函数中递归地合并它们,但是你应该注意不要从任何一个源修改dicts,并且最可靠的方法是在分配值时复制它们。由于密钥必须是可清洗的,因此通常是不可变的,因此复制它们是没有意义的:

{**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: {}}}

性能较差但正确的Ad-hoc

这些方法性能较差,但它们会提供正确的行为。它们的性能远低于my answer to the canonical question on a "Dictionaries of dictionaries merge"copy或者新的解包,因为它们在更高的抽象层次上遍历每个键值对,但它们确实尊重优先顺序(后面的dicts优先)

你也可以在dict理解中手动链接dicts:

update

或者在python 2.6中(当引入生成器表达式时可能早在2.4):

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

dict((k, v) for d in dicts for k, v in d.items()) 将以正确的顺序将迭代器链接到键值对:

itertools.chain

绩效分析

我只会对已知行为正确的用法进行性能分析。

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

以下是在Ubuntu 14.04上完成的

在Python 2.7(系统Python)中:

import timeit

在Python 3.5(deadsnakes PPA)中:

>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934

字典资源


61
投票

我不能使用副本时可以想到的最佳版本是:

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

它比from itertools import chain x = {'a':1, 'b': 2} y = {'b':10, 'c': 11} dict(chain(x.iteritems(), y.iteritems())) 快,但没有dict(x.items() + y.items())快,至少在CPython上。如果你将n = copy(a); n.update(b)更改为iteritems(),这个版本也适用于Python 3,这是由2to3工具自动完成的。

就个人而言,我最喜欢这个版本,因为它在单一功能语法中描述了我想要的东西。唯一的小问题是,从y的值优先于x的值,并没有完全明显,但我不认为很难弄明白。


51
投票
items()

对于具有两个词典('b')中的键的项目,您可以通过将最后一个放在最后一个来控制哪一个最终出现在输出中。


51
投票

Python 3.5(PEP 448)允许更好的语法选项:

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

甚至

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

41
投票

虽然问题已经多次回答,但这个问题的简单解决方案尚未列出。

final = {'a': 1, 'b': 1, **x, **y}

它与z0和上面提到的邪恶z2一样快,但易于理解和改变。


40
投票
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)

在这些阴暗和可疑的答案中,这个光辉的例子是合并Python中的dicts的唯一好方法,由生活的独裁者Guido van Rossum自己赞同!其他人建议这一半,但没有把它放在一个功能。

def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

得到:

print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})

33
投票

如果你认为lambdas是邪恶的,那就不要再读了。根据要求,您可以使用一个表达式编写快速且内存有效的解决方案:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

如上所述,使用两行或编写函数可能是更好的方法。


27
投票

是pythonic。使用x = {'a':1, 'b':2} y = {'b':10, 'c':11} z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y) print z {'a': 1, 'c': 11, 'b': 10} print x {'a': 1, 'b': 2}

comprehension

25
投票

在python3中,z={i:d[i] for d in [x,y] for i in d} >>> print z {'a': 1, 'c': 11, 'b': 10} 方法items,而是一个视图,其作用类似于一组。在这种情况下,你需要采用set union,因为与no longer returns a list连接将不起作用:

+

对于2.7版本中类似python3的行为,dict(x.items() | y.items()) 方法应该代替viewitems

items

不管怎样我更喜欢这种表示法,因为把它想象成一个联合操作而不是连接似乎更自然(如标题所示)。

编辑:

python 3还有几点。首先,请注意dict(x.viewitems() | y.viewitems()) 技巧在python 3中不起作用,除非dict(x, **y)中的键是字符串。

此外,Raymond Hettinger的Chainmap y非常优雅,因为它可以使用任意数量的dicts作为参数,但是answer看起来它依次查看每个查找的所有dicts的列表:

查找会连续搜索基础映射,直到找到密钥。

如果您的应用程序中有大量查找,这会降低您的速度:

from the docs

所以查找速度要慢一个数量级。我是Chainmap的粉丝,但在可能有很多查找的地方看起来不那么实用。


19
投票

滥用导致In [1]: from collections import ChainMap In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo)) In [3]: chainmap_dict = ChainMap(y, x) In [4]: union_dict = dict(x.items() | y.items()) In [5]: timeit for k in union_dict: union_dict[k] 100000 loops, best of 3: 2.15 µs per loop In [6]: timeit for k in chainmap_dict: chainmap_dict[k] 10000 loops, best of 3: 27.1 µs per loop 的单表达式解决方案:

Matthew's answer

你说你想要一个表达式,所以我滥用>>> x = {'a':1, 'b': 2} >>> y = {'b':10, 'c': 11} >>> z = (lambda f=x.copy(): (f.update(y), f)[1])() >>> z {'a': 1, 'c': 11, 'b': 10} 来绑定一个名字,并使用元组来覆盖lambda的一个表达式限制。随意畏缩。

如果你不关心复制它,你当然也可以这样做:

lambda

19
投票

使用保留顺序的itertools的简单解决方案(后面的dicts优先)

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}

它的用法:

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

1533
投票

在您的情况下,您可以做的是:

Modern Python Dictionaries, A Confluence of Great Ideas

这将根据你的需要,将最终的字典放在z = dict(x.items() + y.items()) 中,并使第二个(z)字典的值正确覆盖关键b的值:

y

如果你使用Python 3,它只是稍微复杂一点。要创建>>> x = {'a':1, 'b': 2} >>> y = {'b':10, 'c': 11} >>> z = dict(x.items() + y.items()) >>> z {'a': 1, 'c': 11, 'b': 10}

z

19
投票

两本词典

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

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

n字典

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

def union(*dicts): return dict(itertools.chain.from_iterable(dct.items() for dct in dicts)) 表现不佳。见sum


15
投票

尽管答案对于这个浅薄的字典是好的,但这里定义的方法实际上都没有进行深度字典合并。

示例如下:

https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/

人们会期待这样的结果:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

相反,我们得到这个:

{ 'one': { 'extra': False', 'depth_2': True }, 'two': True }

“one”条目应该具有“depth_2”和“extra”作为其字典中的项目,如果它真的是合并的话。

使用链也不起作用:

{'two': True, 'one': {'extra': False}}

结果是:

from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))

rcwesick给出的深度合并也会产生相同的结果。

是的,它可以合并样本字典,但它们都不是合并的通用机制。一旦我编写了一个执行真正合并的方法,我将在稍后更新。


10
投票

借鉴这里和其他地方的想法,我理解了一个功能:

{'two': True, 'one': {'extra': False}}

用法(在python 3中测试):

def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }

你可以使用lambda代替。


10
投票

我在今天列出的解决方案中遇到的问题是,在合并的字典中,键“b”的值是10但是,按照我的想法,它应该是12.在这种情况下,我提出以下内容:

assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\
    {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'})

assert (merge(foo='bar')=={'foo': 'bar'})

assert (merge({1:11},{1:99},foo='bar',baz='quux')==\
    {1: 99, 'foo': 'bar', 'baz':'quux'})

assert (merge({1:11},{1:99})=={1: 99})

Results:

import timeit

n=100000
su = """
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
"""

def timeMerge(f,su,niter):
    print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f)

timeMerge("dict(x, **y)",su,n)
timeMerge("x.update(y)",su,n)
timeMerge("dict(x.items() + y.items())",su,n)
timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n)

#confirm for loop adds b entries together
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]
print "confirm b elements are added:",x

9
投票
0.049465 sec for: dict(x, **y)
0.033729 sec for: x.update(y)                   
0.150380 sec for: dict(x.items() + y.items())   
0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]

confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}

这应该可以解决您的问题。


8
投票
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

8
投票

这可以通过单个字典理解来完成:

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

在我看来,“单一表达”部分的最佳答案是不需要额外的功能,而且很短。


8
投票

(仅适用于Python2.7 *; Python3 *有更简单的解决方案。)

如果您不反对导入标准库模块,则可以这样做

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> { key: y[key] if key in y else x[key]
      for key in set(x) + set(y)
    }

from functools import reduce def merge_dicts(*dicts): return reduce(lambda a, d: a.update(d) or a, dicts, {}) 中的or a位是必要的,因为lambda总是在成功时返回dict.update。)


6
投票

这是如此愚蠢,以至于None什么也没有回报。 我只是使用一个简单的帮助函数来解决问题:

.update

例子:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

4
投票

使用词典理解,你可以

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

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

dc = {xi:(x[xi] if xi not in list(y.keys()) 
           else y[xi]) for xi in list(x.keys())+(list(y.keys()))}

请注意理解中>>> dc {'a': 1, 'c': 11, 'b': 10} 的语法

if else

596
投票

替代:

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

4
投票

我知道这并不适合问题的具体细节(“单线”),但由于上述答案都没有达到这个方向,而很多答案都解决了性能问题,我觉得我应该贡献自己的想法。

根据用例,可能没有必要创建给定输入词典的“真实”合并字典。在许多情况下,这样做的观点可能就足够了,i。即像合并字典一样行事的对象不会完全计算它。可以这么说,合并字典的懒惰版本。

在Python中,这很简单,可以使用我帖子末尾显示的代码完成。这个给出,原始问题的答案是:

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }

当使用这个新对象时,它将表现得像一个合并的字典,但它将具有恒定的创建时间和恒定的内存占用,同时保持原始字典不受影响。创建它比提出的其他解决方案更便宜。

当然,如果你经常使用结果,那么在某些时候你会达到限制,在这里创建一个真正的合并字典会是更快的解决方案。正如我所说,这取决于你的用例。

如果你觉得你更愿意拥有一个真正合并的z = MergeDict(x, y) ,那么调用dict会产生它(但当然比其他解决方案更昂贵,所以这值得一提)。

您还可以使用此类创建一种写时复制字典:

dict(z)

这是a = { 'x': 3, 'y': 4 } b = MergeDict(a) # we merge just one dict b['x'] = 5 print b # will print {'x': 5, 'y': 4} print a # will print {'y': 4, 'x': 3} 的直接代码:

MergeDict

307
投票

另一个更简洁的选择:

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

注意:这已成为一个流行的答案,但重要的是要指出,如果z = dict(x, **y) 有任何非字符串键,这一点的工作原理是滥用CPython实现细节,它在Python 3中不起作用,或者在PyPy,IronPython或Jython中。还有,y。所以我不能推荐这种技术用于前向兼容或交叉实现的可移植代码,这实际上意味着它应该完全避免。


190
投票

这可能不是一个流行的答案,但你几乎肯定不想这样做。如果你想要一个合并的副本,那么使用copy(或Guido is not a fan,取决于你想要的),然后更新。这两行代码比使用.items()+ .items()的单行创建更具可读性 - 更多Pythonic。显式优于隐式。

此外,当您使用.items()(Python 3.0之前)时,您正在创建一个包含dict项目的新列表。如果你的词典很大,那么这就是很多开销(一旦创建合并的dict就会抛弃两个大的列表)。 update()可以更有效地工作,因为它可以逐项运行第二个dict。

deepcopy而言:

time

IMO前两者之间的微小减速对于可读性来说是值得的。此外,字典创建的关键字参数仅在Python 2.3中添加,而copy()和update()将在旧版本中使用。


132
投票

在后续回答中,您询问了这两种备选方案的相对表现:

>>> 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

在我的机器上,至少(相当普通的x86_64运行Python 2.5.2),替代z1 = dict(x.items() + y.items()) z2 = dict(x, **y) 不仅更短更简单,而且速度更快。您可以使用Python附带的z2模块自行验证。

示例1:将20个连续整数映射到自身的相同字典:

timeit

% 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 以3.5左右的成绩获胜。不同的词典似乎产生了截然不同的结果,但z2似乎总是领先。 (如果同一测试的结果不一致,请尝试使用大于默认值3的数字传入z2。)

示例2:非重叠字典将252个短字符串映射为整数,反之亦然:

-r

% 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 赢得了大约10倍。这在我的书中是一个相当大的胜利!

在比较了这两个之后,我想知道z2的糟糕表现是否可归因于构建两个项目列表的开销,这反过来又让我想知道这种变化是否会更好:

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 z3稍快,但不如z1快。绝对不值得所有额外打字。

这个讨论仍然缺少一些重要的东西,这是对这些替代方案的性能比较与合并两个列表的“明显”方法:使用z2方法。为了尝试使表达式保持平等,没有一个修改x或y,我将复制x而不是就地修改它,如下所示:

update

一个典型的结果:

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 z0似乎具有基本相同的性能。你认为这可能是巧合吗?我不....

事实上,我甚至声称纯Python代码不可能做得更好。如果你可以在C扩展模块中做得更好,我想Python人员可能会对将你的代码(或你的方法的变体)合并到Python核心感兴趣。 Python在很多地方使用z2;优化其运营是一件大事。

你也可以这样写

dict

正如Tony所做的那样,但(并不奇怪)表示法中的差异结果表明不会对性能产生任何可衡量的影响。使用适合您的任何一种。当然,他指出双语句版本更容易理解是绝对正确的。


97
投票

我想要类似的东西,但能够指定复制键上的值是如何合并的,所以我将其破解(但没有对它进行大量测试)。显然这不是单个表达式,而是单个函数调用。

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

91
投票

在Python 3中,您可以使用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 将多个dicts或其他映射组合在一起以创建单个可更新视图:

collections.ChainMap

71
投票

Recursively/deep update a dict

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = ChainMap({}, y, x)
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11

示范:

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)

谢谢rednaw的编辑。

© www.soinside.com 2019 - 2024. All rights reserved.