Как объединить два словаря 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 для всех диктов. например данные диктанты 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), его трудно читать, он не предназначен для использования и поэтому не является 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) используется как "крутой хак" для "call
x.update (y) и return 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 (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
Ресурсы по словарям