У меня есть два словаря 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()
я также ищу решение конфликтов с последними победами .)
python
dictionary
merge
Карл Мейер
источник
источник
z = x | y
Ответы:
Для словарей
x
иy
, словарьz
становится мелко объединенным со значениями,y
заменяющими значения изx
.В Python 3.5 или выше:
В Python 2 (или 3.4 или ниже) напишите функцию:
и сейчас:
В Python 3.9.0a4 или более (окончательная дата выпуска примерно октябрь 2020): PEP-584 , обсуждается здесь , был реализован для дальнейшего упрощения этого:
объяснение
Скажем, у вас есть два диктата, и вы хотите объединить их в новый, не изменяя исходные:
Желаемый результат - получить новый словарь (
z
) со слитыми значениями, а значения второго dict перезаписывают значения из первого.Новый синтаксис для этого, предложенный в PEP 448 и доступный с Python 3.5 ,
И это действительно единственное выражение.
Обратите внимание, что мы можем объединить и с буквенной нотацией:
и сейчас:
В настоящее время он показывает, как реализовано в графике выпуска 3.5, PEP 478 , и теперь он пробился в Что нового в Python 3.5 .
Тем не менее, поскольку многие организации все еще используют Python 2, вы можете сделать это обратно совместимым способом. Классически Pythonic способ, доступный в Python 2 и Python 3.0-3.4, состоит в том, чтобы сделать это как двухэтапный процесс:
В обоих подходах
y
будет второе, и его значения будут заменятьx
значения, поэтому'b'
будут указывать на3
наш конечный результат.Еще не на Python 3.5, но хочу одно выражение
Если вы еще не используете Python 3.5 или вам нужно написать обратно совместимый код, и вы хотите, чтобы это было в одном выражении , самый эффективный и правильный подход - поместить его в функцию:
и тогда у вас есть одно выражение:
Вы также можете создать функцию для слияния неопределенного числа диктовок от нуля до очень большого числа:
Эта функция будет работать в Python 2 и 3 для всех диктов. например , данные dicts
a
вg
:и ключевые пары значений в
g
будет иметь приоритет над dictsa
доf
, и так далее.Критика других ответов
Не используйте то, что вы видите в ранее принятом ответе:
В Python 2 вы создаете два списка в памяти для каждого dict, создаете третий список в памяти с длиной, равной длине первых двух вместе взятых, а затем отбрасываете все три списка для создания dict. В Python 3 это не удастся, потому что вы добавляете два
dict_items
объекта вместе, а не два списка -и вам придется явно создавать их в виде списков, например
z = dict(list(x.items()) + list(y.items()))
. Это пустая трата ресурсов и вычислительной мощности.Точно так же принятие объединения
items()
в Python 3 (viewitems()
в Python 2.7) также не удастся, если значения являются объектами, которые не подлежат изменению (например, списки). Даже если ваши значения являются хэшируемыми, так как наборы семантически неупорядочены, поведение не определено в отношении приоритета. Так что не делайте этого:Этот пример демонстрирует, что происходит, когда значения не различимы:
Вот пример, где у должен иметь приоритет, но вместо этого значение из x сохраняется из-за произвольного порядка множеств:
Еще один хак, который вы не должны использовать:
При этом используется
dict
конструктор, и он очень быстрый и экономит память (даже немного больше, чем наш двухэтапный процесс), но если вы точно не знаете, что здесь происходит (то есть второй dict передается в качестве аргументов ключевого слова dict конструктор), его трудно читать, это не предполагаемое использование, и поэтому это не Pythonic.Вот пример использования исправления в django .
Dicts предназначены для получения хешируемых ключей (например, frozensets или кортежей), но этот метод не работает в Python 3, когда ключи не являются строками.
Из списка рассылки , Гвидо ван Россум, создатель языка, писал:
а также
Это мое понимание (а также понимание создателя языка ), что предполагаемое использование
dict(**y)
для создания диктов в целях читабельности, например:вместо
Ответ на комментарии
Опять же, это не работает для 3, когда ключи не являются строками. Неявный контракт вызова заключается в том, что пространства имен принимают обычные диктовки, в то время как пользователи должны передавать только ключевые аргументы, которые являются строками. Все другие призывные силы принуждали его.
dict
нарушил эту последовательность в Python 2:Это несоответствие было плохим, учитывая другие реализации Python (Pypy, Jython, IronPython). Таким образом, это было исправлено в Python 3, так как это использование может быть серьезным изменением.
Я утверждаю, что это злонамеренная некомпетентность - намеренно писать код, который работает только в одной версии языка или работает только при определенных произвольных ограничениях.
Больше комментариев:
Мой ответ: на
merge_two_dicts(x, y)
самом деле кажется мне намного понятнее, если мы действительно обеспокоены читаемостью. И это не совместимо с форвардом, так как Python 2 все больше и больше не рекомендуется.Да. Я должен отослать вас обратно к вопросу, который требует мелкого слияния двух словарей, причем значения первого перезаписываются значениями второго - в одном выражении.
Предполагая два словаря словарей, один может рекурсивно объединить их в одну функцию, но вы должны быть осторожны, чтобы не изменять указания из любого источника, и самый надежный способ избежать этого - сделать копию при присваивании значений. Поскольку ключи должны быть хэшируемыми и, следовательно, обычно неизменяемыми, копировать их бессмысленно:
Применение:
Решение непредвиденных обстоятельств для других типов значений выходит за рамки этого вопроса, поэтому я укажу вам на мой ответ на канонический вопрос «Словари слияния словарей» .
Менее производительный, но правильный Ad-hocs
Эти подходы менее эффективны, но они обеспечат правильное поведение. Они будут намного менее производительными, чем
copy
иupdate
или новая распаковка, потому что они перебирают каждую пару ключ-значение на более высоком уровне абстракции, но они делают соблюдать порядок старшинства (последние dicts имеют преимущество)Вы также можете связать слова вручную в их понимании:
или в Python 2.6 (и, возможно, уже в 2.4, когда были введены выражения генератора):
itertools.chain
объединит итераторы в пары ключ-значение в правильном порядке:Анализ производительности
Я собираюсь провести анализ производительности только тех случаев, когда известно, что они ведут себя правильно.
Следующее сделано в Ubuntu 14.04
В Python 2.7 (система Python):
В Python 3.5 (deadsnakes PPA):
Ресурсы по словарям
источник
{**{(0, 1):2}}
->{(0, 1): 2}
z = {**x, **y}
действительно стимулируют меняx | y
В вашем случае вы можете сделать следующее:
Это, как вы хотите, вставит последний dict
z
и заставит значение для ключаb
быть должным образом переопределено значением второго (y
) dict:Если вы используете Python 3, это будет немного сложнее. Для создания
z
:Если вы используете Python версии 3.9.0a4 или выше, вы можете напрямую использовать:
источник
Альтернатива:
источник
Update
это не одна из «основных» функций, которые люди часто используют.(lambda z: z.update(y) or z)(x.copy())
: PДругой, более краткий вариант:
Примечание : это стало популярным ответом, но важно отметить, что если
y
есть какие-либо нестроковые ключи, то, что это работает вообще, является злоупотреблением деталями реализации CPython, и это не работает в Python 3, или в PyPy, IronPython или Jython. Кроме того, Гвидо не фанат . Поэтому я не могу рекомендовать эту технику для совместимого с прямым переносом кода или переносимого кода для перекрестной реализации, что на самом деле означает, что его следует полностью избегать.источник
Это, вероятно, не будет популярным ответом, но вы почти наверняка не хотите этого делать. Если вы хотите копию, которая является слиянием, то используйте копию (или Deepcopy , в зависимости от того, что вы хотите), а затем обновите. Две строки кода гораздо более читабельны - более Pythonic - чем создание одной строки с помощью .items () + .items (). Явное лучше, чем неявное.
Кроме того, когда вы используете .items () (до Python 3.0), вы создаете новый список, содержащий элементы из dict. Если ваши словари большие, то это довольно много накладных расходов (два больших списка, которые будут выброшены, как только будет создан объединенный диктат). update () может работать более эффективно, потому что он может проходить через второй элемент dict элемент за элементом.
С точки зрения времени :
ИМО крошечное замедление между первыми двумя стоит его для удобочитаемости. Кроме того, ключевые аргументы для создания словаря были добавлены только в Python 2.3, тогда как copy () и update () будут работать в более старых версиях.
источник
В последующем ответе вы спросили об относительной эффективности этих двух альтернатив:
По крайней мере, на моей машине (довольно обычный x86_64 с Python 2.5.2) альтернатива
z2
не только короче и проще, но и значительно быстрее. Вы можете убедиться в этом сами, используяtimeit
модуль, который поставляется с Python.Пример 1: идентичные словари, отображающие 20 последовательных целых чисел на себя:
z2
выигрывает в 3,5 раза или около того. Различные словари, кажется, дают совершенно разные результаты, ноz2
всегда, кажется, выходят вперед. (Если вы получаете противоречивые результаты для одного и того же теста, попробуйте ввести-r
число, превышающее значение по умолчанию 3.)Пример 2: неперекрывающиеся словари, отображающие 252 короткие строки в целые числа и наоборот:
z2
выигрывает примерно в 10 раз. Это довольно большая победа в моей книге!После сравнения этих двух я подумал,
z1
можно ли объяснить низкую производительность из-за накладных расходов на создание двух списков элементов, что, в свою очередь, заставило меня задуматься, может ли этот вариант работать лучше:Несколько быстрых тестов, например
привести меня к выводу, что
z3
это несколько быстрееz1
, но не так быстро, какz2
. Определенно не стоит всего лишнего набора текста.В этом обсуждении все еще отсутствует что-то важное, а именно сравнение производительности этих альтернатив с «очевидным» способом объединения двух списков: с использованием
update
метода. Чтобы попытаться удержать вещи в равных условиях с выражениями, ни одно из которых не изменяет x или y, я собираюсь сделать копию x вместо ее изменения на месте следующим образом:Типичный результат:
Другими словами,
z0
и,z2
кажется, имеют практически идентичные характеристики. Как вы думаете, это может быть совпадением? Я не....На самом деле, я бы сказал, что для чистого кода Python невозможно сделать что-то лучше этого. И если вы можете значительно улучшить работу модуля расширения C, я думаю, что пользователи Python вполне могут быть заинтересованы во включении вашего кода (или варианта вашего подхода) в ядро Python. Python использует
dict
во многих местах; Оптимизация его операций это большое дело.Вы также можете написать это как
как и Тони, но (что неудивительно) разница в обозначениях не оказывает заметного влияния на производительность. Используйте то, что подходит вам. Конечно, он абсолютно прав, указывая на то, что версию с двумя утверждениями гораздо легче понять.
источник
items()
не поддается лечению иiteritems
не существует.В Python 3.0 и более поздних версиях вы можете использовать то,
collections.ChainMap
какие группы объединяют несколько dicts или других отображений вместе, чтобы создать одно обновляемое представление:Обновление для Python 3.5 и более поздних версий : вы можете использовать расширенную упаковку и распаковку словаря PEP 448 . Это быстро и просто:
источник
del
on, скажем, ChainMap c удалит первое сопоставление этого ключа.dict
чтобы избежать этого, то есть:dict(ChainMap({}, y, x))
Я хотел что-то похожее, но с возможностью указать, как значения на дубликатах ключей должны быть объединены, поэтому я взломал это (но не сильно протестировал это). Очевидно, что это не одно выражение, но это единственный вызов функции.
источник
Рекурсивно / глубокое обновление диктата
Демонстрация:
Выходы:
Спасибо Rednaw за правки.
источник
Лучшая версия, которую я мог бы подумать, не используя копию:
Это быстрее чем,
dict(x.items() + y.items())
но не так быстро, какn = copy(a); n.update(b)
, по крайней мере, на CPython. Эта версия также работает в Python 3, если вы изменитеiteritems()
наitems()
, что автоматически делается инструментом 2to3.Лично мне эта версия нравится больше всего, потому что она достаточно хорошо описывает то, что я хочу, в едином функциональном синтаксисе. Единственная небольшая проблема заключается в том, что не очевидно, что значения от y имеют приоритет над значениями от x, но я не верю, что это трудно понять.
источник
Python 3.5 (PEP 448) допускает более приятную синтаксическую опцию:
Или даже
В Python 3.9 вы также используете | и | = с приведенным ниже примером из PEP 584
источник
dict(x, **y)
? Как вы (@CarlMeyer) упомянули в примечании к своему собственному ответу ( stackoverflow.com/a/39858/2798610 ), Гвидо считает это решение незаконным .dict(x, **y)
по (очень хорошей) причине, что он полагаетсяy
только на наличие ключей, которые являются допустимыми именами аргументов ключевых слов (если вы не используете CPython 2.7, где конструктор dict обманывает). Это возражение / ограничение не относится к PEP 448, который обобщает**
синтаксис распаковки для наложения литералов. Таким образом, это решение имеет тот же краткий характер, чтоdict(x, **y)
и без недостатков.Для элементов с ключами в обоих словарях ('b') вы можете контролировать, какой из них окажется в выводе, поместив этот последний.
источник
Хотя на этот вопрос уже был дан ответ несколько раз, это простое решение проблемы еще не было перечислено.
Это так же быстро, как z0 и зло z2, упомянутое выше, но легко понять и изменить.
источник
z4 = {}
и измените следующую строку наz4 = x.copy()
- лучше, чем просто хороший код не делает ненужных вещей (что делает его еще более читаемым и обслуживаемым).Среди таких сомнительных и сомнительных ответов этот яркий пример - единственный и единственный хороший способ объединить диктанты в Python, одобренный диктатором на всю жизнь Гвидо ван Россумом ! Кто-то предложил половину этого, но не включил его в работу.
дает:
источник
Если вы думаете, что лямбды - это зло, не читайте дальше. По запросу вы можете написать быстрое и эффективное для памяти решение с одним выражением:
Как предложено выше, лучше всего использовать две строки или написать функцию.
источник
Будьте питоничны. Используйте понимание :
источник
def dictmerge(*args): return {i:d[i] for d in args for i in d}
z={k: v for d in (x, y) for k, v in d.items()}
В python3
items
метод больше не возвращает список , а скорее представление , которое действует как набор. В этом случае вам нужно взять объединение множеств, так как конкатенация с+
не будет работать:Для Python3-подобного поведения в версии 2.7
viewitems
метод должен работать вместоitems
:В любом случае, я предпочитаю это обозначение, так как кажется более естественным думать о нем как о операции объединения множеств, а не о конкатенации (как показывает заголовок).
Редактировать:
Еще пара моментов для Python 3. Во-первых, обратите внимание, что
dict(x, **y)
уловка не будет работать в Python 3, если ключи вy
являются строками.Кроме того, ответ Рэймонда Хеттингера на Chainmap довольно элегантен, поскольку он может принимать в качестве аргументов произвольное количество диктов, но из документов он выглядит так, будто последовательно просматривает список всех диктов для каждого поиска:
Это может замедлить вас, если в вашем приложении много поисков:
Так что примерно на порядок медленнее для поисков. Я фанат Chainmap, но выглядит менее практичным там, где может быть много поисков.
источник
Злоупотребление, приводящее к решению с одним выражением для ответа Мэтью :
Вы сказали, что хотите одно выражение, поэтому я злоупотребил
lambda
связыванием имени и кортежами, чтобы переопределить ограничение на одно выражение лямбды. Не стесняйтесь съеживаться.Конечно, вы также можете сделать это, если не хотите копировать это:
источник
Простое решение с использованием itertools, которое сохраняет порядок (последние имеют приоритет)
И это использование:
источник
Два словаря
русский словарь
sum
имеет плохую производительность. Смотрите https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/источник
Хотя ответы были хороши для этого мелкого словаря, ни один из методов, определенных здесь, на самом деле не выполняет глубокое слияние словаря.
Примеры следуют:
Можно ожидать, что-то вроде этого:
Вместо этого мы получаем это:
Элемент 'one' должен был иметь значения 'deep_2' и 'extra' в качестве элементов внутри своего словаря, если он действительно был слиянием.
Использование цепочки также не работает:
Результаты в:
Глубокое слияние, которое дал rcwesick, также создает тот же результат.
Да, это будет работать для объединения образцов словарей, но ни один из них не является универсальным механизмом объединения. Я обновлю это позже, когда напишу метод, который выполняет истинное слияние.
источник
(Только для Python2.7 *; для Python3 * существуют более простые решения.)
Если вы не против импортировать стандартный модуль библиотеки, вы можете сделать
(
or a
Бит вlambda
необходим, потому чтоdict.update
всегда возвращаетсяNone
в случае успеха.)источник
Если не возражаешь, мутируя
x
,Простой, читаемый, производительный. Вы знаете,
update()
всегда возвращаетNone
, что является ложным значением. Таким образом, приведенное выше выражение всегда будет оцениватьсяx
после его обновления.Методы мутации в стандартной библиотеке (например
.update()
) возвращаютсяNone
по соглашению, поэтому этот шаблон будет работать и с ними. Если вы используете метод, который не следует этому соглашению, онor
может не работать. Но вы можете использовать отображение и индекс кортежа, чтобы сделать его одним выражением. Это работает независимо от того, что оценивает первый элемент.Если у вас еще нет
x
переменной, вы можете использовать ееlambda
для создания локального объекта без использования оператора присваивания. Это равносильно использованиюlambda
в качестве выражения let , что является обычной техникой в функциональных языках, но, возможно, не пифонично.Хотя это не сильно отличается от следующего использования оператора new walrus (только Python 3.8+):
Если вам нужна копия, стиль PEP 448 - самый простой
{**x, **y}
. Но если это недоступно в вашей (более старой) версии Python, здесь также работает шаблон let .(Это, конечно, эквивалентно
(z := x.copy()).update(y) or z
, но если ваша версия Python достаточно новая для этого, тогда стиль PEP 448 будет доступен.)источник
Опираясь на идеи здесь и в других местах, я понял функцию:
Использование (протестировано в Python 3):
Вы можете использовать лямбду вместо этого.
источник
Проблема, с которой я столкнулся с решениями, перечисленными на сегодняшний день, заключается в том, что в объединенном словаре значение ключа «b» равно 10, но, по моему мнению, оно должно быть 12. В этом свете я представляю следующее:
Результаты:
источник
cytoolz.merge_with
( toolz.readthedocs.io/en/latest/... )Это так глупо, что
.update
ничего не возвращает.Я просто использую простую вспомогательную функцию для решения проблемы:
Примеры:
источник
Это должно решить вашу проблему.
источник
Это можно сделать с помощью единого понимания:
На мой взгляд, лучший ответ для части с «одним выражением», так как никаких дополнительных функций не требуется, и это короткий.
источник
Благодаря PEP 572: Выражения назначения появится новая опция при выпуске Python 3.8 ( запланировано на 20 октября 2019 г. ) . Новый оператор выражения присваивания позволяет вам присвоить результат и по-прежнему использовать его для вызова , оставляя объединенный код одним выражением, а не двумя операторами, изменяя:
:=
copy
update
чтобы:
при этом ведя себя одинаково во всех отношениях. Если вы также должны вернуть полученный результат
dict
(вы запросили выражение, возвращающееdict
; вышеприведенное создает и присваиваетnewdict
, но не возвращает его, поэтому вы не могли использовать его для передачи аргумента функции как есть, а-ляmyfunc((newdict := dict1.copy()).update(dict2))
) , затем просто добавьтеor newdict
в конец (так какupdate
возвращаетNone
, что ложно, он будет затем оценивать и возвращатьnewdict
в качестве результата выражения):Важное предостережение: в целом, я бы не рекомендовал такой подход в пользу:
Подход к распаковке более понятен (всем, кто в первую очередь знает об общей распаковке, что вам следует ), вообще не требует имени для результата (так что он гораздо более лаконичен при создании временного объекта, который немедленно передается функция или включена в
list
/tuple
literal или тому подобное), и почти наверняка также быстрее, будучи (на CPython) примерно эквивалентным:но выполняется на уровне C, с использованием конкретного
dict
API, поэтому не(newdict := dict1.copy()).update(dict2)
требуется никакого динамического поиска / привязки метода или накладных расходов на диспетчеризацию вызова (где поведение неизбежно идентично исходному двухуровневому поведению, выполнение работы дискретными шагами, с динамическим поиском / привязка / вызов методов.Это также более расширяемый, так как объединение трех
dict
s очевидно:где использование выражений присваивания не будет так масштабироваться; самое близкое, что вы могли бы получить:
или без временного кортежа
None
s, но с проверкой достоверности каждогоNone
результата:любой из которых, очевидно , гораздо уродливее, и включает в себя дополнительные неэффективных (либо впустую временный
tuple
изNone
с запятой для разделения, или бессмысленных тестирований truthiness каждогоupdate
«ыNone
возвращения дляor
разделения).Единственное реальное преимущество в подходе выражения присваивания имеет место, если:
set
s, так иdict
s (оба поддерживаютcopy
иupdate
, поэтому код работает примерно так, как вы ожидаете)dict
себя, и должны сохранить тип и семантику левой части (вместо того, чтобы заканчивать простымdict
). Хотя этоmyspecialdict({**speciala, **specialb})
может сработать, это будет связано с дополнительным временным использованиемdict
, и если уmyspecialdict
него есть обычные функции,dict
которые не могут быть сохранены (например, обычныеdict
s теперь сохраняют порядок, основанный на первом появлении ключа, и значение, основанное на последнем появлении ключа; вы можете захотеть тот, который сохраняет порядок на основе последнегопоявление ключа, поэтому обновление значения также перемещает его в конец), тогда семантика будет неправильной. Поскольку версия выражения присваивания использует именованные методы (которые, по-видимому, перегружены для правильного поведения), она никогда не создаетdict
вообще (еслиdict1
это уже не былоdict
), сохраняя исходный тип (и семантику исходного типа), избегая при этом любых временных.источник
источник
x
своей копией. Еслиx
это аргумент функции, это не сработает (см. Пример )