Как объединить два словаря в одном выражении? - PullRequest
3974 голосов
/ 02 сентября 2008

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

Ответы [ 40 ]

14 голосов
/ 04 августа 2012

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

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

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

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

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

Результаты:

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

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

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

10 голосов
/ 19 июля 2013

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

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 голосов
/ 03 декабря 2013

Проблема с решениями, перечисленными на сегодняшний день, заключается в том, что в объединенном словаре значение ключа "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 голосов
/ 28 марта 2016

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

8 голосов
/ 30 ноября 2015
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

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

8 голосов
/ 13 ноября 2013
>>> 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 голосов
/ 17 июля 2015

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

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

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

6 голосов
/ 02 марта 2014

Это так глупо, что .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
4 голосов
/ 27 мая 2013

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

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() }
4 голосов
/ 22 сентября 2017

Если не возражаете, мутируйте x,

x.update(y) or x

Простой, читабельный, производительный. Вы знаете, update() всегда возвращает None, что является ложным значением. Так что он всегда будет оцениваться как x.

Методы мутации в стандартной библиотеке, такие как update, возвращают None по соглашению, так что этот прием тоже будет работать с ними.

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

(x.update(y), x)[-1]

Если у вас еще нет x в переменной, вы можете использовать lambda, чтобы создать локальный объект без оператора присваивания. Это равносильно использованию lambda в качестве let выражения , что является распространенным методом в функциональных языках, но довольно непифоническим.

(lambda x: x.update(y) or x)({'a':1, 'b': 2})

Если вам нужна копия, лучше всего PEP 448 {**x, **y}. Но если это не доступно, let работает и здесь.

(lambda z: z.update(y) or z)(x.copy())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...