Как объединить два словаря в одно выражение?

у меня есть два словаря Python, и я хочу написать одно выражение, которое возвращает эти два словаря, объединенные. The update() метод был бы тем, что мне нужно, если бы он вернул свой результат вместо изменения dict на месте.

>>> 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() это то, что я тоже ищу.)

30 ответов


как объединить два словаря Python в одном выражении?

словари x и y, z становится Объединенным словарем со значениями из y замена тех из x.

  • в Python 3.5 или более, :

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • в 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)
    

объяснение

скажем, у вас есть два Дикта, и вы хотите объединить их в новый дикт, не изменяя оригинальные дикты:

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

желаемый результат-получить новый словарь (z) с Объединенными значениями, А значения второго Дикта перезаписывают значения из первого.

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

новый синтаксис для этого, предлагаемые в PEP 448 и доступно с Python 3.5, is

z = {**x, **y}

и это действительно одно выражение. Теперь он показывает, как реализовано в график выпуска для 3.5, PEP 478, и теперь он пробрался в что нового в Python 3.5 документ.

однако, поскольку многие организации все еще находятся на Python 2, Вы можете сделать это обратно совместимым образом. Классический Питонический способ, доступный в Python 2 и Python 3.0-3.4, это сделать как двухэтапный процесс:

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)

вы также можете сделать функцию для объединения неопределенного количества диктов, от нуля до очень большого числа:

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

эта функция будет работать в Python 2 и 3 для всех словарь. например, данные dicts a to g:

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

и пары ключевых значений в g будет иметь приоритет над dicts a to f и так на.

критика других ответов

не используйте то, что вы видите в ранее принятом ответе:

z = dict(x.items() + y.items())

в Python 2 вы создаете два списка в памяти для каждого Дикта, создаете третий список в памяти с длиной, равной длине первых двух вместе взятых, а затем отбрасываете все три списка для создания Дикта. в Python 3 это не удастся потому что вы добавляете два 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'

и вам придется явно создавать их в виде списков, например z = dict(list(x.items()) + list(y.items())). Это пустая трата ресурсов и энергии.

аналогично, принимая Союза items() в Python 3 (viewitems() в Python 2.7) также не удастся, когда значения unhashable объекты (такие как списки, например). Даже если ваши значения хэшируются,поскольку множества семантически неупорядочены, поведение не определено в отношении приоритета. Так не делай это:

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

в этом примере показано, что происходит, когда значения unhashable:

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

вот пример, где y должен иметь приоритет, но вместо этого значение из x сохраняется из-за произвольного порядка множеств:

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

другой Хак вы не должны использовать:

z = dict(x, **y)

использует dict конструктор, и очень быстрый и эффективная память (даже немного больше, чем наш двухэтапный процесс), но если вы точно знаете, что здесь происходит (то есть, второй дикт передается в качестве аргумента в конструктор сайта дикт), трудно читать, это не целевое использование, а так это не подходящие для Python.

вот пример использованияисправлено в Джанго.

Dicts предназначены для использования хэшируемых ключей (например, frozensets или кортежи), но этот метод не работает в Python 3, когда ключи не веревка.

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

с рассылки, Гвидо ван Россум, создатель языка, писал:

я в порядке с объявление dict({}, **{1:3}) незаконно, так как в конце концов это злоупотребление механизм.

и

по-видимому, dict(x, * * y) идет вокруг как "крутой хак" для " вызова X. обновить (y)и вернуть x". Лично я нахожу это более презренным, чем прохладный.

это мое понимание (а также понимание создатель языка), что предполагаемое использование для dict(**y) предназначен для создания диктов для целей удобочитаемости, например:

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

вместо

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

ответ на комментарий

несмотря на то, что говорит Гвидо,dict(x, **y) соответствует спецификации dict, которая кстати. работает как для Python 2 и 3. Дело в том, что это только works for string keys-это прямое следствие того, как работают параметры ключевых слов, а не короткое замыкание dict. Также использование оператора * * в этом месте не является злоупотреблением механизмом, фактически * * был разработан именно для передачи диктовок в качестве ключевых слов.

опять же, это не работает для 3, когда ключи не являются строками. Неявный контракт вызова заключается в том, что пространства имен принимают обычные дикты, в то время как пользователи должны передавать только аргументы ключевых слов, которые являются строками. Все остальные callables принудили его. dict нарушил эту согласованность в Python 2:

>>> 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 (Pypy, Jython, IronPython). Таким образом, это было исправлено в Python 3, так как это может быть критическим изменением.

я представляю вам, что это злонамеренная некомпетентность намеренно писать код, который работает только в одной версии языка или который работает только с определенными произвольными ограничениями.

еще один комментарий:

dict(x.items() + y.items()) по-прежнему является наиболее читаемым решением для Python 2. Считываемость считается.

мой ответ: merge_two_dicts(x, y) на самом деле мне кажется гораздо яснее, если мы действительно обеспокоены удобочитаемостью. И он не совместим с forward, так как Python 2 становится все более устаревшим.

менее эффективный, но правильный Ad-hocs

эти подходы менее эффективны, но они обеспечат правильное поведение. Они будут гораздо меньше исполнительнее, чем copy и update или новая распаковка, потому что они повторяют каждую пару ключ-значение на более высоком уровне абстракции, но они do уважать порядок приоритета (последние дикты имеют приоритет)

вы также можете связать диктовки вручную внутри понимания дикт:

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

или в python 2.6 (и, возможно, уже 2.4, когда были введены выражения генератора):

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

Анализ Производительности

я только собираюсь сделать анализ производительности обычаев, которые, как известно, ведут себя правильно.

import timeit

следующее сделано на Ubuntu 14.04

в Python 2.7 (язык Python):

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

в Python 3.5 (deadsnakes PPA):

>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287

ресурсы по словарям


в вашем случае, что вы можете сделать, это:

z = dict(x.items() + y.items())

это будет, как вы хотите, поставить окончательный дикт в z, и сделайте значение для ключа b правильно переопределяется вторым (y) значение Дикта:

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

если вы используете Python 3, это только немного сложнее. Для создания z:

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

альтернатива:

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

другой, более краткий, вариант:

z = dict(x, **y)

Примечание: это стало популярным ответом, но важно отметить, что если y имеет любые нестроковые ключи, тот факт, что это работает вообще, является злоупотреблением деталью реализации CPython, и он не работает в Python 3 или в PyPy, IronPython или Jython. Кроме того,Гвидо не фанат. Поэтому я не могу рекомендовать этот метод для переносимого кода с прямой совместимостью или перекрестной реализацией, который на самом деле это означает, что его следует полностью избегать.


Это, вероятно, не будет популярен ответ, но вы почти наверняка не хотите этого делать. Если вам нужна копия, которая является слиянием, используйте copy (или deepcopy в зависимости от того, что вы хотите) и затем обновить. Две строки кода гораздо более читабельным - более подходящие для Python, чем один создание линии .поставки. + )(поставки.)( Явное лучше, чем неявное.

кроме того, при использовании .items () (pre Python 3.0), вы создаете новый список, содержащий пункты из дикт. Если ваши словари большие, то это довольно много накладных расходов (два больших списка, которые будут выброшены, как только будет создан объединенный дикт). update () может работать более эффективно, потому что он может работать через второй элемент dict по элементам.

С точки зрения времени:

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

IMO крошечное замедление между первыми двумя стоит того, чтобы читать. Кроме того, аргументы ключевых слов для создания словаря были добавлены только в Python 2.3, тогда как copy() и update () будут работать в более старых версиях.


в последующем ответе вы спросили об относительной производительности этих двух альтернатив:

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

на моей машине, по крайней мере (довольно обычный x86_64 под управлением Python 2.5.2), альтернатива z2 не только короче и проще, но и значительно быстрее. Вы можете проверить это для себя, используя timeit модуль, который поставляется с Python.

Пример 1: идентичные словари отображение 20 последовательных целых чисел в сами:

% 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

z2 выигрывает в 3,5 раза или около того. Разные словари, кажется, дают совершенно разные результаты, но z2 всегда, кажется, выходит вперед. (Если вы получаете противоречивые результаты для то же самое тест, попробуйте пройти в -r С числом больше, чем по умолчанию 3.)

Пример 2: неперекрывающиеся словари отображение 252 коротких строк в целые числа и наоборот:

% 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

z2 выигрывает около a фактор 10. Это довольно большая победа в моей книге!

после сравнения этих двух, я подумал, если 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. Определенно не стоит дополнительный набор текста.

в этом обсуждении все еще отсутствует что-то важное, что является сравнением производительности этих альтернатив с "очевидным" способом объединения двух списков: с помощью update метод. Чтобы попытаться держать вещи на равных с выражениями, ни одно из которых не изменяет x или y, я собираюсь сделать копию x вместо того, чтобы изменять ее на месте, следующим образом:

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 и z2 кажется, имеют практически одинаковые показатели. Думаешь, это совпадение? Я не....

на самом деле, я бы пошел так далеко, чтобы утверждать, что чистый код Python не может сделать лучше, чем это. И если вы можете сделать значительно лучше в модуле расширения C, я полагаю, что люди Python могут быть заинтересованы в включении вашего кода (или вариации вашего подхода) в ядро Python. Python использует dict во многих местах; оптимизация операции-это большое дело.

вы также можете написать это как

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

как и Тони, но (неудивительно) разница в обозначениях оказывается не оказывает никакого измеримого влияния на производительность. Используйте то, что кажется вам правильным. Конечно, он абсолютно прав, указывая, что версию с двумя утверждениями гораздо легче понять.


Я хотел что-то подобное, но с возможностью указать, как значения на дубликатах ключей были объединены, поэтому я взломал это (но не сильно тестировал его). Очевидно, что это не одно выражение, но это один вызов функции.

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

в Python 3, Вы можете использовать сборники.ChainMap какие группы несколько диктов или другие сопоставления вместе, чтобы создать один, обновляемый вид:

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

рекурсивно / глубокое обновление dict

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

спасибо rednaw за редактирование.


лучшая версия, которую я мог бы подумать, не используя копию, будет:

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()) но не так быстро, как n = copy(a); n.update(b), по крайней мере на CPython. Эта версия также работает в Python 3, Если вы измените iteritems() to items(), что автоматически выполняется инструментом 2to3.

лично мне больше нравится эта версия, потому что она описывает довольно хорошо, что я хочу в одном функциональном синтаксисе. Единственная незначительная проблема заключается в том, что это не делает совершенно очевидным, что значения из y имеют приоритет над значениями из x, но я не думаю, что это трудно понять.


Python 3.5 (PEP 448) позволяет более приятный синтаксис:

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}

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

для элементов с ключами в обоих словарях ('b') вы можете контролировать, какой из них заканчивается на выходе, поставив его последним.


хотя вопрос уже ответили несколько раз, это простое решение проблемы еще не было перечислено.

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

это так же быстро, как z0 и зло z2, упомянутое выше, но легко понять и изменить.


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

new = dict_merge(old, extras)

среди таких теневых и сомнительных ответов этот яркий пример-единственный хороший способ объединить диктанты в Python, одобренный диктатором на всю жизнь Гвидо ван Россум! Кто-то другой предложил половину этого, но не поместил ее в функцию.

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

выдает:

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

Если вы думаете, что лямбды-зло, тогда не читайте дальше. По запросу вы можете написать быстрое и эффективное для памяти решение с одним выражением:

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}

как было предложено выше, использование двух строк или запись функции, вероятно, лучший способ пойти.


быть обновления. Используйте понимание:

z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}

в питон3, в items метод больше не возвращает список, а посмотреть, который действует как установить. В этом случае вам нужно будет взять установленное объединение, так как конкатенация с + не работает:

dict(x.items() | y.items())

для python3-подобного поведения в версии 2.7,viewitems метод должен работать вместо items:

dict(x.viewitems() | y.viewitems())

Я предпочитаю эту нотацию в любом случае, поскольку кажется более естественным думать об этом как о заданной операции объединения чем конкатенация (как показывает заголовок).

Edit:

еще пара очков для python 3. Во-первых, обратите внимание, что dict(x, **y) трюк не будет работать в Python 3, если ключи в y несколько строк.

кроме того, Chainmap ответ довольно элегантно, так как он может принимать произвольное количество диктов в качестве аргументов, но документы похоже, что он последовательно просматривает список всех диктов для каждого поиска:

поиск поиск базовых сопоставлений последовательно, пока не будет найден ключ.

это может замедлить вас, если у вас есть много запросов в вашем приложении:

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

таким образом, на порядок медленнее для поиска. Я поклонник Chainmap, но выглядит менее практично, где может быть много поисков.


злоупотребление, приводящее к решению с одним выражением для Мэтью!--5-->:

>>> 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 для привязки имени и кортежей для переопределения ограничения одного выражения лямбды. Не стесняйтесь съеживаться.

вы также можете сделать это, конечно, если вы не заботитесь о его копировании:

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

простое решение с использованием itertools, которое сохраняет порядок (последние дикты имеют приоритет)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, 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}

два словари

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

n словари

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

sum имеет плохую производительность. См.https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/


в python 3:

import collections
a = {1: 1, 2: 2}
b = {2: 3, 3: 4}
c = {3: 5}

r = dict(collections.ChainMap(a, b, c))
print(r)

Out:

{1: 1, 2: 2, 3: 4}

Docs:https://docs.python.org/3/library/collections.html#collections.ChainMap:


хотя ответы были хороши для этого мелкий словарь, ни один из методов, определенных здесь, на самом деле не делает глубокое слияние словаря.

примеры:

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

запись " one "должна была иметь "depth_2" и "extra" в качестве элементов внутри своего словаря, если это действительно было слияние.

использование цепи также делает не работает:

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

результаты:

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

глубокое слияние, которое дал rcwesick, также создает тот же результат.

Да, он будет работать для объединения образцов словарей, но ни один из них не является общим механизмом для объединения. Я обновлю это позже, как только напишу метод, который выполняет истинное слияние.


Для Python 2 :

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

Для Python 3:

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

Это дает выход:{'a': 1, 'c': 11, 'b': 10}


опираясь на идеи здесь и в других местах, я поняла функция:

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

использование (протестировано в python 3):

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

проблема, которую я имею с решениями, перечисленными на сегодняшний день, заключается в том, что в объединенном словаре значение для ключа "b" равно 10, но, по моему мнению, оно должно быть 12. В этом свете я представляю следующее:--3-->

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}

Это можно сделать с помощью одного понимания Дикта:

>>> 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 collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

Это должно решить вашу проблему.


(Для Python2.7 * только; есть более простые решения для Python3*.)

если вы не прочь импортировать стандартный библиотечный модуль, вы можете сделать

from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})

(том or a бита lambda нужно, потому что dict.update всегда возвращает None на успех.)


в Python 3.5 вы можете использовать unpack ** для создания нового словаря. Этот метод не был показан в прошлых ответах. Кроме того, лучше использовать {} вместо dict(). Потому что {} является литералом python и dict() включает вызов функции.

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}