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

2663
голосов

У меня есть два словаря 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() - это то, что я ищу.)

задан Carl Meyer 02 сент. '08 в 10:44
источник

42 ответов

  • 1
  • 2
2485
голосов

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

Описание

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

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

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

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

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

z = {**x, **y}

И это действительно одно выражение. Теперь он показан как реализованный в расписании релиза Что нового в документе 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)

Вы также можете создать функцию для объединения числа undefined dicts, от нуля до очень большого числа:

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

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

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

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

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

z = dict(x, **y)

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

Здесь пример использования исправлен в django.

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

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

Из списка рассылки, создатель языка, Guido van Rossum написал:

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

и

По-видимому, dict (x, ** y) идет как "классный взлом" для "вызова" x.update(y) и return 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. Также не используется оператор ** в этом месте, злоупотребление механизмом, на самом деле ** было разработано именно для передачи dicts в качестве ключевых слов.

Опять же, он не работает для 3, когда клавиши не являются строками. Контракт неявного вызова заключается в том, что пространства имен принимают обычные dicts, тогда как пользователи должны передавать только аргументы ключевых слов, которые являются строками. Все остальные вызовы принудительно его применяли. 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 все больше устаревает.

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

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

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

{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 11 нояб. '14 в 1:11
источник
1383
голосов

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

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}
ответ дан Thomas Vander Stichele 02 сент. '08 в 10:50
источник
517
голосов

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

z = x.copy()
z.update(y)
ответ дан Matthew Schinckel 02 сент. '08 в 16:00
источник
244
голосов

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

z = dict(x, **y)

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

ответ дан Carl Meyer 02 сент. '08 в 18:52
источник
153
голосов

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

ответ дан Tony Meyer 08 сент. '08 в 14:16
источник
100
голосов

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

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)

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

ответ дан zaphod 23 окт. '08 в 5:38
источник
74
голосов

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

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

В 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
ответ дан Raymond Hettinger 28 апр. '13 в 6:15
источник
49
голосов

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

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 в 21:55
источник
49
голосов

Рекурсивно/глубокое обновление 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 за изменения.

ответ дан Stan 29 нояб. '11 в 14:52
источник
35
голосов
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

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

ответ дан Greg Hewgill 02 сент. '08 в 10:49
источник
32
голосов

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 27 февр. '15 в 0:27
источник
28
голосов

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

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

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

ответ дан phobie 14 окт. '11 в 19:12
источник
26
голосов
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 авг. '12 в 12:24
источник
21
голос

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

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 в 21:08
источник
14
голосов

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

ответ дан beardc 09 окт. '13 в 21:09
источник
11
голосов

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

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

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

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

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

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

Два словаря

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/

ответ дан Mathieu Larose 17 окт. '12 в 5:09
источник
8
голосов

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

Примеры:

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

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

ответ дан Thanh Lim 04 авг. '12 в 2:36
источник
7
голосов
>>> 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 нояб. '13 в 13:01
источник
6
голосов

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

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

Вы можете использовать toolz.merge([x, y]) для этого.

ответ дан Mike Graham 18 нояб. '16 в 15:53
источник
6
голосов

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

Для 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 авг. '16 в 16:53
источник
5
голосов

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

ответ дан Skyduy 24 мая '17 в 10:24
источник
4
голосов
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

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

ответ дан user208859 30 нояб. '15 в 16:04
источник
4
голосов

(только для 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 марта '16 в 16:13
источник
4
голосов

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

>>> 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 июля '15 в 17:47
источник
  • 1
  • 2

Другие вопросы по меткам