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

У меня есть два словаря 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()я также ищу решение конфликтов с последними победами .)

2.09.2008 07:44:30
30 ОТВЕТОВ
РЕШЕНИЕ

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

Для словарей xи y, словарь zстановится мелко объединенным со значениями, 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)
  • В Python 3.9.0a4 или более (окончательная дата выпуска примерно октябрь 2020): PEP-584 , обсуждается здесь , был реализован для дальнейшего упрощения этого:

    z = x | y          # NOTE: 3.9+ ONLY

объяснение

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

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

Желаемый результат - получить новый словарь ( z) со слитыми значениями, а значения второго dict перезаписывают значения из первого.

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

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

z = {**x, **y}

И это действительно одно выражение.

Обратите внимание, что мы можем объединить и с буквенной нотацией:

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

и сейчас:

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

Теперь он показывает, как реализовано в графике выпуска 3.5, PEP 478 , и теперь он появился в документе « Что нового в Python 3.5» .

Однако, поскольку многие организации все еще работают на Python 2, вы можете сделать это обратно совместимым способом. Классически Pythonic способ, доступный в 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в g:

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

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

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

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

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

В Python 2 вы создаете два списка в памяти для каждого dict, создаете третий список в памяти с длиной, равной длине первых двух вместе взятых, а затем отбрасываете все три списка для создания dict. В 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) также не удастся, если значения являются объектами, которые не подлежат изменению (например, списки). Даже если ваши значения являются хэшируемыми, так как наборы семантически неупорядочены, поведение не определено в отношении приоритета. Так что не делайте этого:

>>> 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 сохраняется из-за произвольного порядка множеств:

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

Еще один хак, который вы не должны использовать:

z = dict(x, **y)

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

Вот пример использования исправления в Django .

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.update (y) и возврата x». Лично я нахожу это более презренным, чем крутым.

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

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

вместо того

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

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

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

Опять же, это не работает для 3, когда ключи не являются строками. Неявный контракт вызова заключается в том, что пространства имен принимают обычные диктовки, в то время как пользователи должны передавать только ключевые аргументы, которые являются строками. Все другие призывные силы принуждали его. 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)самом деле кажется мне намного понятнее, если мы действительно обеспокоены читаемостью. И это не совместимо с форвардом, так как Python 2 все более и более устарел.

{**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-hocs

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

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

{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

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

5508
4.03.2020 15:24:49
Ограничение только строк для расширения ключевых слов достаточно, чтобы исключить {**x, **y}метод. Тем не менее, itemsподход можно сделать работоспособным путем преобразования dictitemsв listподобное dict(list(x.items()), list(y.items())).
Mohammad Azim 16.05.2019 15:00:10
@MohammadAzim «только строки» применяется только к раскрытию аргумента ключевого слова в вызываемых объектах, а не к обобщенному синтаксису распаковки. Чтобы продемонстрировать, что это работает: {**{(0, 1):2}}->{(0, 1): 2}
Aaron Hall♦ 16.05.2019 16:07:57
короткие ответы, такие, как z = {**x, **y}действительно стимулируют меня
pcko1 10.10.2019 18:19:46
@ cal97g да, я ответил на это в своем ответе около 10 дней назад: stackoverflow.com/posts/26853961/revisions
Aaron Hall♦ 28.02.2020 16:38:11
Ах да, профсоюзный оператор! Если это работает для наборов, почему это не должно работать для диктов? Очень жду PEP 584.
Arthur Khazbs 5.03.2020 00:08:07
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

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

59
2.09.2008 07:49:27
В Python 3 вы получите TypeError: неподдерживаемый тип (ы) операндов для +: 'dict_items' и 'dict_items' ... вы должны заключать каждый dict в список (), например: dict (list (x.items ()) + list (y.items ()))
justSaid 26.04.2019 08:45:27

В вашем случае вы можете сделать следующее:

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

Это, как вы захотите, вставит последний dict zи заставит значение для ключа bбыть должным образом переопределено значением second ( 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 3, это будет немного сложнее. Для создания z:

>>> z = dict(list(x.items()) + list(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
print(z)

Output: {'a': 1, 'c': 11, 'b': 10}
1596
10.04.2020 06:46:06
Не используйте это, поскольку это очень неэффективно. (См. Результаты этого времени ниже.) Возможно, в дни Py2 это было необходимо, если функция-обертка не была опцией, но эти дни уже прошли.
Gringo Suave 11.03.2020 17:22:45

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

z = x.copy()
z.update(y)
626
2.09.2008 13:00:46
Чтобы выяснить, почему это не соответствует критерию, указанному в вопросе: это не одно выражение, и оно не возвращает z.
Alex 21.03.2013 13:15:45
@neuronet каждый oneliner, как правило, просто перемещает код, который должен попасть в другой компонент, и решает его там. это определенно один из случаев. но другие языки имеют более приятные конструкции, чем python для этого. и иметь ссылочно прозрачный вариант, который возвращает его элемент, приятно иметь вещь.
Alex 19.10.2017 12:38:45
Говоря так: если вам нужно поместить две строки комментариев, объясняющих вашу одну строку кода, людям, которым вы передаете свой код ... Вы действительно сделали это одной строкой? :) Я полностью согласен, что Python не годится для этого: должен быть намного более простой путь. Хотя этот ответ более питоничен, действительно ли он настолько ясен или ясен? Updateэто не одна из «основных» функций, которые люди часто используют.
eric 19.10.2017 13:07:09
Что ж, если люди настаивают на том, чтобы сделать его единоличным, вы всегда можете сделать это (lambda z: z.update(y) or z)(x.copy()): P
towr 24.02.2020 12:43:23

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

z = dict(x, **y)

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

335
21.01.2016 06:43:24
Прекрасно работает в Python 3 и PyPy и PyPy 3 , не может общаться с Jython или Iron. Учитывая, что этот шаблон явно задокументирован (см. Третью форму конструктора в этой документации), я бы сказал, что это не «деталь реализации», а преднамеренное использование функций.
amcgregor 12.04.2019 13:10:50
@amcgregor Вы пропустили ключевую фразу «если у y есть нестроковые ключи». Это то, что не работает в Python3; тот факт, что он работает в CPython 2, является деталью реализации, на которую нельзя положиться. Если все ваши ключи гарантированно являются строками, это полностью поддерживаемая опция.
Carl Meyer 10.05.2019 16:27:02

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

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
111
13.09.2014 19:56:21

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

Кроме того, когда вы используете .items () (до Python 3.0), вы создаете новый список, содержащий элементы из dict. Если ваши словари большие, то это довольно много накладных расходов (два больших списка, которые будут выброшены, как только будет создан объединенный диктат). 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

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

210
5.08.2014 23:56:02

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

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 выигрывает примерно в 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)

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

147
10.01.2015 02:32:55
Это не работает в Python 3; items()не подлежит кованию и iteritemsне существует.
Antti Haapala 16.03.2015 05:50:54

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

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()на items(), что автоматически делается инструментом 2to3.

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

70
14.10.2010 18:55:15

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

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

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

48
14.10.2011 16:12:33
но это три утверждения, а не одно выражение
fortran 18.10.2011 15:44:51
Да! Упомянутые решения с одним выражением являются либо медленными, либо злыми. Хороший код удобен для чтения и сопровождения. Таким образом, проблема - это вопрос, а не ответ. Мы должны просить лучшего решения проблемы, а не однострочного решения.
phobie 28.10.2011 03:36:17
Потеряйте z4 = {}и измените следующую строку на z4 = x.copy()- лучше, чем просто хороший код не делает ненужных вещей (что делает его еще более читаемым и обслуживаемым).
martineau 8.03.2013 15:10:15
Ваше предложение изменит это на ответ Мэтьюса. Хотя его ответ хорош, я думаю, что мой лучше читается и лучше поддерживается. Дополнительная строка будет плохой, только если это будет стоить времени выполнения.
phobie 6.05.2013 11:50:17

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

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}

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

38
23.11.2011 18:20:48

Рекурсивно / глубокое обновление диктанта

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 за правки.

85
18.12.2015 11:19:15
Это не отвечает на вопрос. Вопрос явно требует нового словаря z из исходных словарей x и y, где значения y заменяют значения x, а не обновленный словарь. Этот ответ изменяет y на месте, добавляя значения из x. Хуже того, он не копирует эти значения, поэтому можно дополнительно изменить измененный словарь y, и изменения могут быть отражены в словаре x. @ Jérôme Я надеюсь, что этот код не вызывает ошибок в вашем приложении - по крайней мере, подумайте об использовании deepcopy для копирования значений.
Aaron Hall♦ 9.11.2018 02:14:04
@AaronHall согласился, что это не отвечает на вопрос. Но это отвечает моей потребности. Я понимаю эти ограничения, но в моем случае это не проблема. Думая об этом, может быть, название вводит в заблуждение, поскольку оно может вызвать глубокую копию, которую он не предоставляет. Но это касается глубокого вложения. Вот еще одна реализация из Martellibot: stackoverflow.com/questions/3232943/… .
Jérôme 9.11.2018 13:24:32

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

Примеры следуют:

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' должен был иметь значения 'deep_2' и 'extra' в качестве элементов внутри своего словаря, если это действительно слияние.

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

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

Результаты в:

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

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

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

16
3.08.2012 23:36:50
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'}
46
6.08.2012 09:30:07

Два словаря

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

21
2.10.2016 18:16:17

В Python 3.0 и более поздних версиях вы можете использовать collections.ChainMapгруппы, которые объединяют несколько 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

Обновление для Python 3.5 и более поздних версий : вы можете использовать расширенную упаковку и распаковку словаря PEP 448 . Это быстро и просто:

>>> x = {'a':1, 'b': 2}
>>> y = y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}
122
22.04.2020 12:46:36
Но следует соблюдать осторожность при использовании ChainMap, но есть одна загвоздка в том, что если у вас есть дубликаты ключей, используются значения из первого сопоставления, а когда вы вызываете a, delскажем, ChainMap c удалит первое сопоставление этого ключа.
Slayer 14.02.2017 05:14:37
@Prerit Что еще можно ожидать от этого? Это нормальный способ работы цепочек имен. Посмотрите, как работает $ PATH в bash. Удаление исполняемого файла по пути не препятствует другому исполняемому файлу с тем же именем в последующем.
Raymond Hettinger 15.02.2017 07:24:40
@Raymond Hettinger Я согласен, только что добавил осторожность. Большинство людей могут не знать об этом. : D
Slayer 15.02.2017 15:15:26
@Prerit Вы могли бы использовать, чтобы dictизбежать этого, то есть:dict(ChainMap({}, y, x))
wjandrea 15.07.2019 16:30:35

Используя понимание, вы можете

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в понимании

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }
5
27.05.2013 09:04:20
Мне нравится идея использовать диктовку, но ваша реализация слаба. Это безумие, чтобы использовать ... in list(y.keys())вместо просто ... in y.
wim 18.02.2014 20:18:48

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

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

Вы можете использовать лямбду вместо этого.

10
19.07.2013 05:49:19

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

>>> 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}
22
23.05.2017 12:34:53

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

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

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

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

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

Редактировать:

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

30
23.05.2017 12:34:53
>>> 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
13.11.2013 10:01:31
Этот метод перезаписывается xсвоей копией. Если xэто аргумент функции, это не сработает (см. Пример )
bartolo-otrit 22.02.2019 09:27:27

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

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}
10
3.12.2013 18:11:54
Вы можете быть заинтересованы в cytoolz.merge_with( toolz.readthedocs.io/en/latest/... )
bli 16.02.2017 13:44:30

У меня есть решение, которое не указано здесь

z = {}
z.update(x) or z.update(y)

Это не будет обновлять х, а также у. Спектакль? Я не думаю, что это будет ужасно медленно :-)

ПРИМЕЧАНИЕ. Предполагается, что это «или» операция, а не «и» операция. Отредактировано, чтобы исправить код.

1
29.05.2019 04:13:31
Нееееет, это все еще 2 строки, а также длиннее, чемz = x.copy(); z.update(y)
Navin 11.12.2013 09:50:20
... or z. В противном случае это возвращается None.
clacke 7.04.2018 12:20:52
Я не назначаю операции обновления z:-), но у меня появилась идея сделать их однострочными. Спасибо.
thiruvenkadam 27.06.2018 07:43:46

Это так глупо, что .updateничего не возвращает.
Я просто использую простую вспомогательную функцию для решения проблемы:

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
10
2.03.2014 01:44:39

Объединение двух словарей ОП будет что-то вроде:

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

В частности, объединение двух сущностей ( xи y) содержит все элементы xи / или y. К сожалению, то, что просит ФП, не является профсоюзом, несмотря на название поста.

Мой код ниже не является ни элегантным, ни однострочным, но я считаю, что это согласуется со значением союза.

Из примера ОП:

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

z = {}
for k, v in x.items():
    if not k in z:
        z[k] = [(v)]
    else:
        z[k].append((v))
for k, v in y.items():
    if not k in z:
        z[k] = [(v)]
    else:
        z[k].append((v))

{'a': [1], 'b': [2, 10], 'c': [11]}

Если вы хотите, чтобы списки были изменены, но вышеприведенное будет работать, если словарь содержит списки (и вложенные списки) в качестве значений в любом словаре.

4
10.01.2015 02:21:58
Я отредактировал вопрос, чтобы не использовать это слово union, для ясности.
Carl Meyer 30.09.2014 15:49:34
Возможно, вы имеете в виду {'a': 1, 'b': (2, 10), 'c': 11}...?
Alfe 18.05.2016 16:07:25

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}
65
26.02.2015 21:27:52
Чем это решение лучше, чем решение dict(x, **y)? Как вы (@CarlMeyer) упомянули в примечании к своему собственному ответу ( stackoverflow.com/a/39858/2798610 ), Гвидо считает это решение незаконным .
Blackeagle52 4.03.2015 11:09:13
Гвидо не любит dict(x, **y)по (очень хорошей) причине, что он полагается yтолько на наличие ключей, которые являются допустимыми именами аргументов ключевых слов (если вы не используете CPython 2.7, где конструктор dict обманывает). Это возражение / ограничение не относится к PEP 448, который обобщает **синтаксис распаковки для наложения литералов. Так что это решение имеет тот же краткий характер, что dict(x, **y)и без недостатков.
Carl Meyer 4.03.2015 22:24:44

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

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

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

9
17.07.2015 14:47:23
Я подозреваю, что производительность будет не очень хорошей, хотя; создание набора из каждого dict, а затем только итерация по клавишам означает новый поиск значения каждый раз (хотя и относительно быстрый, но все же увеличивает порядок функции для масштабирования)
Breezer 16.02.2017 14:57:42
все зависит от версии питона, который мы используем. В 3.5 и выше {** x, ** y} дает объединенный словарь
Rashid Mv 23.12.2017 15:50:00

Простое решение с использованием 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}
21
6.09.2016 11:30:16
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

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

10
30.11.2015 13:04:00

Будьте питоничны. Используйте понимание :

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

>>> print z
{'a': 1, 'c': 11, 'b': 10}
32
29.09.2016 10:45:25
Как функция:def dictmerge(*args): return {i:d[i] for d in args for i in d}
Jesse Knight 6.06.2018 18:27:21
Сохраните поиск, выполнив итерацию пар ключ / значение напрямую:z={k: v for d in (x, y) for k, v in d.items()}
ShadowRanger 5.03.2019 19:33:32