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

У меня есть два словаря Python, и я хочу написать одно выражение, которое возвращает эти два словаря, слияние. Метод 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}

Как я могу получить этот окончательный объединенный dict в z, а не x?

(Чтобы быть экстра-ясным, обработка конфликтов с последними однократными конфликтами dict.update() - это то, что я ищу.)

3876
02 сент. '08 в 10:44
источник поделиться
55 ответов
  • 1
  • 2

Как я могу объединить два словаря 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 keys and values
        z.update(y)    # modifies z with y keys and values & returns None
        return z
    

    и сейчас:

    z = merge_two_dicts(x, y)
    

объяснение

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

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 для всех диктов. например, данные диктует от a до g:

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

и пары ключ-значение в g будут иметь приоритет над параметрами от 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 constructor), его трудно читать, это не предполагаемое использование, и поэтому оно не является 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) предназначено для создания dicts в целях читабельности, например:

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 или новая распаковка, потому что они выполняют итерацию по каждой паре ключ-значение на более высоком уровне абстракции, но они уважают порядок приоритета (последние диктаты имеют приоритет)

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

{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

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

4317
11 нояб. '14 в 1:11
источник

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

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

Это, как вы пожелаете, поместите окончательный dict в 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}
1515
02 сент. '08 в 10:50
источник

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

z = x.copy()
z.update(y)
586
02 сент. '08 в 16:00
источник

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

z = dict(x, **y)

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

298
02 сент. '08 в 18:52
источник

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

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

В терминах time:

>>> 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() будут работать в более старых версиях.

182
08 сент. '08 в 14:16
источник

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

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 с номером, большим, чем значение по умолчанию.)

Пример 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)

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

127
23 окт. '08 в 5:38
источник

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

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
93
04 сент. '08 в 22:08
источник

В Python 3 вы можете использовать collections.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
86
28 апр. '13 в 6:15
источник

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

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict 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 за изменения.

68
29 нояб. '11 в 14:52
источник

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

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, но я не считаю, что это трудно понять.

59
14 окт. '10 в 21:55
источник

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}
49
27 февр. '15 в 0:27
источник
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

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

49
02 сент. '08 в 10:49
источник

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

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

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

40
14 окт. '11 в 19:12
источник
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'}
38
06 авг. '12 в 12:24
источник

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

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}

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

31
23 нояб. '11 в 21:08
источник

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

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

>>> print z
{'a': 1, 'c': 11, 'b': 10}
26
20 янв. '16 в 14:46
источник

В python3 метод 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 не являются строками.

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

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

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

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

23
09 окт. '13 в 21:09
источник

Простое решение с использованием 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}
18
04 авг. '15 в 17:54
источник

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

>>> 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}
18
08 авг. '13 в 0:23
источник

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

Из:

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

Документы: https://docs.python.org/3/library/collections.html#collections.ChainMap:

18
24 мая '17 в 10:24
источник

Два словаря

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/

17
17 окт. '12 в 5:09
источник

Для 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}

13
31 авг. '16 в 16:53
источник

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

Примеры:

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

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

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

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

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

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

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

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

13
04 авг. '12 в 2:36
источник

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

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 июля '13 в 8:49
источник

Проблема, которую я имею с решениями, перечисленными на сегодняшний день, заключается в том, что в объединенном словаре значение для ключа "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}
8
03 дек. '13 в 21:11
источник

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

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

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

8
17 июля '15 в 17:47
источник
>>> 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 нояб. '13 в 13:01
источник
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

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

8
30 нояб. '15 в 16:04
источник

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

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}
8
28 сент. '16 в 3:33
источник

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

7
28 марта '16 в 16:13
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос