Как я могу объединить два словаря 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)
В Python 3.9.0a4 или более (окончательная дата выпуска примерно октябрь 2020): PEP-584 , обсуждается здесь , был реализован для дальнейшего упрощения этого:
z = x | y # NOTE: 3.9+ ONLY
объяснение
Скажем, у вас есть два диктата, и вы хотите объединить их в новый, не изменяя исходные:
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
или новая распаковка, потому что они перебирают каждую пару ключ-значение на более высоком уровне абстракции, но они делают соблюдать порядок старшинства (последние dicts имеют преимущество)
Вы также можете связать слова вручную в их понимании:
{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
Ресурсы по словарям
z = x | y