我想将两个词典融入一个新的词典。

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

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

每当两个字典中都有一个关键 k 时,只应保留 y(k) 的值。


当前回答

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

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

z = {**x, **y}

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

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

z = x.copy()
z.update(y) # which returns None since it mutates z

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

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

和 g 的关键值对将先行于字典 a 到 f 等。

z = dict(x.items() + y.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 (viewitems() 在 Python 2.7) 中采取元素的合并也会失败,当值是不可破坏的对象(如列表,例如)。即使您的值是可破坏的,因为套件是无形的,行为与先例无定义。

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

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

字典的目的是采取可触摸的密钥(例如,frozensets或tuples),但这种方法在Python 3中失败,当密钥不是线条时。

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

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

{'a': 1, 'b': 10, 'c': 11}

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

我的答案: merge_two_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: {}}}

{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()) # iteritems in Python 2

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 z

min(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())))

>>> 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.8发布时,将有一个新的选项(计划于2019年10月20日),感谢PEP 572:分配表达式。 新的分配表达式操作员:= 允许您分配副本的结果,并且仍然使用它来呼叫更新,留下组合代码一个单一表达式,而不是两个陈述,改变:

newdict = dict1.copy()
newdict.update(dict2)

到:

(newdict := dict1.copy()).update(dict2)

(newdict := dict1.copy()).update(dict2) or newdict

newdict = {**dict1, **dict2}

脱包方法更清晰(对于任何人都知道一般的脱包,首先,你应该),不需要一个名字的结果(因此,它是更紧密的,当构建一个暂时的,即时转移到一个功能或包含在一个名单 / 双字或类似),并且几乎肯定更快,也是(在CPython上)相当于:

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)

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

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

它像 z0 和 Z2 上述那样快,但很容易理解和改变。

Python 3.9 + 仅限

合并(<unk>)和更新(<unk>=)运营商已被添加到内置的<unk>类。

>>> d = {'spam': 1, 'eggs': 2, 'cheese': 3}
>>> e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
>>> d | e
{'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

扩展任务版本在现场运行:

>>> d |= e
>>> d
{'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

查看 PEP 584

z = MergeDict(x, y)

当使用这个新对象时,它将像合并词典一样行事,但它将有持续的创作时间和持续的记忆脚印,同时让原始词典无触摸。

当然,如果你使用结果很多,那么你会在某个时候达到创建一个真正的合并词典会是最快的解决方案的界限。

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}

class MergeDict(object):
  def __init__(self, *originals):
    self.originals = ({},) + originals[::-1]  # reversed

  def __getitem__(self, key):
    for original in self.originals:
      try:
        return original[key]
      except KeyError:
        pass
    raise KeyError(key)

  def __setitem__(self, key, value):
    self.originals[0][key] = value

  def __iter__(self):
    return iter(self.keys())

  def __repr__(self):
    return '%s(%s)' % (
      self.__class__.__name__,
      ', '.join(repr(original)
          for original in reversed(self.originals)))

  def __str__(self):
    return '{%s}' % ', '.join(
        '%r: %r' % i for i in self.iteritems())

  def iteritems(self):
    found = set()
    for original in self.originals:
      for k, v in original.iteritems():
        if k not in found:
          yield k, v
          found.add(k)

  def items(self):
    return list(self.iteritems())

  def keys(self):
    return list(k for k, _ in self.iteritems())

  def values(self):
    return list(v for _, v in self.iteritems())

使用 Itertools 保持顺序的简单解决方案(后者有先例)

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