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

python dictionary merge

1434204 просмотра

30 ответа

У меня есть два словаря 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}

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

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

Автор: Carl Meyer Источник Размещён: 17.05.2019 02:43

Ответы (30)


4153 плюса

Решение

Как я могу объединить два словаря 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)
    

объяснение

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

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

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

{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

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

Автор: Aaron Hall Размещён: 10.11.2014 10:11

1507 плюса

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

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

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

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

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

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}
Автор: Thomas Vander Stichele Размещён: 02.09.2008 07:50

584 плюса

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

z = x.copy()
z.update(y)
Автор: Matthew Schinckel Размещён: 02.09.2008 01:00

292 плюса

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

z = dict(x, **y)

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

Автор: Carl Meyer Размещён: 02.09.2008 03:52

181 плюса

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

Автор: Tony Meyer Размещён: 08.09.2008 11:16

125 плюса

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

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)

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

Автор: zaphod Размещён: 23.10.2008 02:38

92 плюса

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

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
Автор: rcreswick Размещён: 04.09.2008 07:08

85 плюса

В Python 3 вы можете использовать collection.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
Автор: Raymond Hettinger Размещён: 28.04.2013 03:15

68 плюса

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

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

Автор: Stan Размещён: 29.11.2011 11:52

59 плюса

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

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

Автор: driax Размещён: 14.10.2010 06:55

48 плюса

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

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

Автор: Greg Hewgill Размещён: 02.09.2008 07:49

48 плюса

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}
Автор: Bilal Syed Hussain Размещён: 26.02.2015 09:27

40 плюса

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

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

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

Автор: phobie Размещён: 14.10.2011 04:12

38 плюса

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'}
Автор: Sam Watkins Размещён: 06.08.2012 09:24

31 плюса

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

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}

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

Автор: EMS Размещён: 23.11.2011 06:08

25 плюса

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

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

>>> print z
{'a': 1, 'c': 11, 'b': 10}
Автор: Robino Размещён: 20.01.2016 11:46

23 плюса

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

Автор: beardc Размещён: 09.10.2013 06:09

18 плюса

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

>>> 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}
Автор: Claudiu Размещён: 07.08.2013 09:23

17 плюса

Простое решение с использованием 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}
Автор: reubano Размещён: 04.08.2015 02:54

16 плюса

Два словаря

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/

Автор: Mathieu Larose Размещён: 17.10.2012 02:09

16 плюса

В питоне 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 :

Автор: Skyduy Размещён: 24.05.2017 07:24

13 плюса

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

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

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, также создает тот же результат.

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

Автор: Thanh Lim Размещён: 03.08.2012 11:36

12 плюса

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

Автор: Kalpesh Dusane Размещён: 31.08.2016 01:53

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

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

Автор: Bijou Trouvaille Размещён: 19.07.2013 05:49

8 плюса

>>> 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}
Автор: John La Rooy Размещён: 13.11.2013 10:01

8 плюса

Проблема, с которой я столкнулся с решениями, перечисленными на сегодняшний день, состоит в том, что в объединенном словаре значение ключа "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}
Автор: upandacross Размещён: 03.12.2013 06:11

8 плюса

Это может быть сделано с одним пониманием:

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

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

Автор: RemcoGerlich Размещён: 17.07.2015 02:47

8 плюса

from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

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

Автор: reetesh11 Размещён: 30.11.2015 01:04

8 плюса

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

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}
Автор: levi Размещён: 28.09.2016 12:33

7 плюса

(Только для 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в случае успеха.)

Автор: kjo Размещён: 28.03.2016 01:13
Вопросы из категории :
32x32