Python 如何在一个表达式中合并两个词典(使用词典的并集)?

Python 如何在一个表达式中合并两个词典(使用词典的并集)?,python,dictionary,merge,Python,Dictionary,Merge,我有两个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} 我怎样

我有两个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()
的冲突处理,这也是我想要的。)

对于两个字典('b')中都有键的项,您可以通过最后一个来控制输出中的键


对于两个字典(“b”)中都有键的项目,您可以通过最后一个来控制输出中的键。

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

z=dict(list(x.items())+list(y.items())
这将根据您的需要,将最终dict放入
z
,并使键
b
的值被第二个(
y
)dict的值正确覆盖:

>x={'a':1,'b':2}
>>>y={'b':10,'c':11}
>>>z=dict(列表(x.items())+列表(y.items())
>>>z
{'a':1,'c':11,'b':10}
如果使用Python 2,甚至可以删除
list()
调用。要创建z,请执行以下操作:

z=dict(x.items()+y.items()) >>>z {'a':1,'c':11,'b':10} 如果使用Python 3.9.0a4或更高版本,则可以直接使用:

x={'a':1,'b':2}
y={'b':10,'c':11}
z=x | y
打印(z)
{'a':1,'c':11,'b':10}

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

z=dict(list(x.items())+list(y.items())
这将根据您的需要,将最终dict放入
z
,并使键
b
的值被第二个(
y
)dict的值正确覆盖:

>x={'a':1,'b':2}
>>>y={'b':10,'c':11}
>>>z=dict(列表(x.items())+列表(y.items())
>>>z
{'a':1,'c':11,'b':10}
如果使用Python 2,甚至可以删除
list()
调用。要创建z,请执行以下操作:

z=dict(x.items()+y.items()) >>>z {'a':1,'c':11,'b':10} 如果使用Python 3.9.0a4或更高版本,则可以直接使用:

x={'a':1,'b':2}
y={'b':10,'c':11}
z=x | y
打印(z)
{'a':1,'c':11,'b':10}
备选方案:

z = x.copy()
z.update(y)
另一种选择:

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

另一个更简洁的选项:

z = dict(x, **y)

注意:这已成为一个流行的答案,但需要指出的是,如果
y
有任何非字符串键,那么这一工作原理就是滥用了CPython实现细节,在Python 3或PyPy、IronPython或Jython中不起作用。也。因此,我不能将这种技术推荐给前向兼容或跨实现的可移植代码,这实际上意味着应该完全避免使用这种技术。

另一个更简洁的选项:

z = dict(x, **y)

注意:这已成为一个流行的答案,但需要指出的是,如果
y
有任何非字符串键,那么这一工作原理就是滥用了CPython实现细节,在Python 3或PyPy、IronPython或Jython中不起作用。也。因此,我不能将这种技术推荐给前向兼容或跨实现的可移植代码,这实际上意味着应该完全避免使用这种技术。

我想要类似的技术,但能够指定如何合并重复键上的值,所以我解决了这个问题(但没有对其进行大量测试)。显然,这不是一个单一的表达式,而是一个单一的函数调用

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

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

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

这可能不是一个流行的答案,但您几乎肯定不想这样做。如果您想要一个合并的副本,那么使用copy(或者,取决于您想要的),然后更新。这两行代码比使用.items()+.items()创建的单行代码可读性更强,更具Python风格。显式比隐式好

此外,当您使用.items()(Python 3.0之前的版本)时,您正在创建一个新列表,其中包含来自dict的项。如果您的字典很大,那么这将是相当大的开销(创建合并的dict后将立即丢弃两个大列表)。update()可以更高效地工作,因为它可以逐项运行第二个dict

在以下方面:


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

这可能不是一个流行的答案,但您几乎肯定不想这样做。如果您想要一个合并的副本,那么使用copy(或者,取决于您想要的),然后更新。这两行代码比使用.items()+.items()创建的单行代码可读性更强,更具Python风格。显式比隐式好

此外,当您使用.items()(Python 3.0之前的版本)时,您正在创建一个新列表,其中包含来自dict的项。如果您的字典很大,那么这将是相当大的开销(创建合并的dict后将立即丢弃两个大列表)。update()可以更高效地工作,因为它可以逐项运行第二个dict

在以下方面:


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

在后续回答中,您询问了这两个选项的相对性能:

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)
至少在我的机器上
% 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
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
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 = x.copy()
z0.update(y)
from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)
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}
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)
{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}
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 }
{'two': True, 'one': {'extra': False}}
from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))
{'two': True, 'one': {'extra': False}}
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'})
{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}
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))
>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(ChainMap({}, y, x))
>>> for k, v in z.items():
        print(k, '-->', v)
    
a --> 1
b --> 10
c --> 11
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x | y
{'a': 1, 'b': 10, 'c': 11}
def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }
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})
>>> 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}
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}
dict(x.items() | y.items())
dict(x.viewitems() | y.viewitems())
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
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
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}
def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1
merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy
>>> 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}
x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}
final = {'a': 1, 'b': 1, **x, **y}
d = {'spam': 1, 'eggs': 2, 'cheese': 3}
e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
d | e
# {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}
>>> 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)
    }
# py2
from itertools import chain, imap
merge = lambda *args: dict(chain.from_iterable(imap(dict.iteritems, args)))

# py3
from itertools import chain
merge = lambda *args: dict(chain.from_iterable(map(dict.items, args)))
>>> 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}
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))
z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}
from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})
x.update(y) or x
(x.update(y), x)[-1]
(lambda x: x.update(y) or x)({'a': 1, 'b': 2})
(x := {'a': 1, 'b': 2}).update(y) or x
(lambda x={'a': 1, 'b': 2}: x.update(y) or x)()
(lambda z=x.copy(): z.update(y) or z)()
newdict = dict1.copy()
newdict.update(dict2)
(newdict := dict1.copy()).update(dict2)
(newdict := dict1.copy()).update(dict2) or newdict
newdict = {**dict1, **dict2}
newdict = {}
newdict.update(dict1)
newdict.update(dict2)
 newdict = {**dict1, **dict2, **dict3}
 (newdict := dict1.copy()).update(dict2), newdict.update(dict3)
 (newdict := dict1.copy()).update(dict2) or newdict.update(dict3)
>>> d = {'a': 1, 'b': 2}
>>> e = {'a': 9, 'c': 3}
>>> d | e
{'a': 9, 'b': 2, 'c': 3}
>>> e |= d    # e = e | d
>>> e
{'a': 1, 'c': 3, 'b': 2}