Мне нужно объединить несколько словарей, вот что у меня, например:
dict1 = {1:{"a":{A}}, 2:{"b":{B}}}
dict2 = {2:{"c":{C}}, 3:{"d":{D}}
С A
B
C
и D
быть листьями дерева, как{"info1":"value", "info2":"value2"}
Неизвестный уровень (глубина) словарей, может быть {2:{"c":{"z":{"y":{C}}}}}
В моем случае он представляет собой структуру каталогов / файлов, где узлы являются документами, а листья - файлами.
Я хочу объединить их, чтобы получить:
dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}
Я не уверен, как мне это легко сделать с Python.
python
dictionary
merge
array-merge
fdhex
источник
источник
y
сплющить доc
уровня что ли? Ваш пример неполный.Ответы:
на самом деле это довольно сложно - особенно если вам нужно полезное сообщение об ошибке, когда что-то несовместимо, при правильном приеме повторяющихся, но непротиворечивых записей (то, что здесь не делает никакой другой ответ ...)
предполагая, что у вас нет большого количества записей, проще всего использовать рекурсивную функцию:
обратите внимание, что это изменяет
a
- содержимоеb
добавляетсяa
(которое также возвращается). если вы хотите сохранить,a
вы можете назвать это какmerge(dict(a), b)
.agf указал (ниже), что у вас может быть более двух dicts, и в этом случае вы можете использовать:
где все будет добавлено в dict1.
[примечание - я отредактировал свой первоначальный ответ, чтобы изменить первый аргумент; что упрощает объяснение "сокращения"]
ps в python 3 вам также понадобится
from functools import reduce
источник
reduce
в цикл или эквивалентный цикл для работы с произвольным числомdict
s вместо двух. Тем не менее, я не уверен, что это делает то, что он хочет (он не был ясен). Вы закончили2: {'c': {'z': {'y': {'info1': 'value', 'info2': 'value2'}}}, 'b': {'info1': 'value', 'info2': 'value2'}}
его вторым примером, я не уверен, хочет ли онz
иy
сглаживать или нет?a[key] = a[key] + b[key]
. Спасибо за полезный ответ.copy.deepcopy
.Вот простой способ сделать это с помощью генераторов:
Это печатает:
источник
Одна из проблем с этим вопросом заключается в том, что значения dict могут быть произвольно сложными фрагментами данных. Основываясь на этих и других ответах, я придумал этот код:
Мой вариант использования - слияние файлов YAML, где мне нужно иметь дело только с подмножеством возможных типов данных. Следовательно, я могу игнорировать кортежи и другие объекты. Для меня разумная логика слияния означает
Все остальное и непредвиденное приводит к ошибке.
источник
isinstance(a, (str, unicode, int, long, float))
нее?Поскольку это канонический вопрос (несмотря на некоторые не общие черты), я предлагаю канонический питонический подход к решению этой проблемы.
Самый простой случай: «листья - это вложенные словари, заканчивающиеся пустыми словами»:
Это самый простой случай рекурсии, и я бы рекомендовал два наивных подхода:
Я считаю, что предпочитаю второе первому, но имейте в виду, что исходное состояние первого должно быть восстановлено из его источника. Вот использование:
Сложный случай: «листья любого другого типа:»
Так что, если они заканчиваются dicts, это простой случай слияния конечных пустых dicts. Если нет, то это не так уж и тривиально. Если строки, как их объединить? Наборы можно обновлять аналогичным образом, чтобы мы могли обработать это, но теряем порядок, в котором они были объединены. Так имеет значение порядок?
Таким образом, вместо получения дополнительной информации, самый простой подход будет заключаться в предоставлении им стандартной процедуры обновления, если оба значения не являются dicts: то есть значение второго dict будет перезаписывать первое, даже если значение второго dict равно None, а значение первого - a dict с большим количеством информации.
И сейчас
возвращается
Применение к исходному вопросу:
Мне пришлось удалить фигурные скобки вокруг букв и заключить их в одинарные кавычки, чтобы это был законный Python (иначе они были бы установлены литералами в Python 2.7+), а также добавить недостающую скобку:
и
rec_merge(dict1, dict2)
теперь возвращается:Какие матчи желаемого результата первоначального вопроса (после изменения, например,
{A}
в'A'
.)источник
По материалам @andrew cooke. Эта версия обрабатывает вложенные списки dicts, а также позволяет обновлять значения
источник
Эта простая рекурсивная процедура объединит один словарь в другой, переопределив конфликтующие ключи:
Вывод:
источник
На основе ответов @andrew cooke. Он лучше заботится о вложенных списках.
источник
Если у вас неизвестный уровень словарей, то я бы предложил рекурсивную функцию:
источник
обзор
Следующий подход подразделяет проблему глубокого слияния диктов на:
Параметризованная неглубокая функция слияния,
merge(f)(a,b)
которая использует функциюf
для слияния двух dictsa
иb
Рекурсивная функция слияния,
f
которая будет использоваться вместе сmerge
Реализация
Функцию слияния двух (не вложенных) dicts можно написать разными способами. Лично мне нравится
Хороший способ определения подходящей рекурсивной функции слияния
f
- использование множественной рассылки, которая позволяет определять функции, которые оцениваются по разным путям в зависимости от типа их аргументов.пример
Чтобы объединить два вложенных словаря, просто используйте,
merge(f)
например:Ноты:
Преимущества такого подхода:
Функция построена из более мелких функций, каждая из которых выполняет одну задачу, что упрощает анализ и тестирование кода.
Поведение не запрограммировано жестко, но может быть изменено и расширено по мере необходимости, что улучшает повторное использование кода (см. Пример ниже).
настройка
В некоторых ответах также рассматривались словари, содержащие списки, например, других (потенциально вложенных) слов. В этом случае может потребоваться сопоставить списки и объединить их в зависимости от положения. Это можно сделать, добавив еще одно определение к функции слияния
f
:источник
Если кому-то нужен еще один подход к этой проблеме, вот мое решение.
Достоинства : краткость, декларативность и функциональность по стилю (рекурсивная, без изменений).
Возможный недостаток : это может быть не то слияние, которое вы ищете. Обратитесь к документации по семантике.
источник
Вы можете попробовать mergedeep .
Монтаж
использование
источник
Есть небольшая проблема с ответом Эндрю Кука: в некоторых случаях он изменяет второй аргумент,
b
когда вы изменяете возвращаемый dict. В частности, из-за этой строки:Если
b[key]
это adict
, он будет просто назначенa
, что означает, что любые последующие изменения этогоdict
затронут какa
иb
.Чтобы исправить это, нужно заменить строку на это:
Где
clone_dict
находится:По-прежнему. Это , очевидно , не учитывает
list
,set
и другие вещи, но я надеюсь , что это иллюстрирует подводные камни при попытке слиянияdicts
.И для полноты картины вот моя версия, в которой вы можете передать ее несколько раз
dicts
:источник
deepcopy
вместоclone_dict
?В этой версии функции будет учитываться количество словарей N и только словари - нельзя передавать неправильные параметры, иначе возникнет ошибка TypeError. Само слияние учитывает конфликты ключей, и вместо перезаписи данных из словаря далее по цепочке слияния оно создает набор значений и добавляет к нему; данные не теряются.
Возможно, он не самый эффективный на странице, но он самый тщательный, и вы не потеряете никакой информации при объединении двух слов в N.
вывод: {1: [1, 2], 2: {1: 2, 3: 1}, 4: 4}
источник
Поскольку dictviews поддерживает операции с наборами, я смог значительно упростить ответ jterrace.
Любая попытка объединить dict с non dict (технически, объект с методом «ключей» и объект без метода «ключей») вызовет ошибку AttributeError. Это включает как начальный вызов функции, так и рекурсивные вызовы. Это именно то, что я хотел, поэтому я оставил это. Вы можете легко поймать AttributeErrors, вызванные рекурсивным вызовом, и затем выдать любое значение, которое вам нравится.
источник
Short-н-сладкий:
Это работает как
dict.update
метод Python (и основан на нем) . Он возвращаетсяNone
(вы всегда можете добавить,return d
если хотите), поскольку он обновляет dictd
на месте. Ключи вv
будут перезаписывать любые существующие ключи вd
(он не пытается интерпретировать содержимое dict).Это также будет работать для других («dict-like») сопоставлений.
источник
Конечно, код будет зависеть от ваших правил разрешения конфликтов слияния. Вот версия, которая может принимать произвольное количество аргументов и рекурсивно объединять их до произвольной глубины без использования каких-либо изменений объекта. Для разрешения конфликтов слияния используются следующие правила:
{"foo": {...}}
имеют приоритет{"foo": "bar"}
){"a": 1}
,{"a", 2}
и{"a": 3}
в порядке, то результат будет{"a": 3}
)источник
У меня было два словаря (
a
иb
), каждый из которых мог содержать любое количество вложенных словарей. Я хотел рекурсивно объединить их сb
приоритетомa
.Рассматривая вложенные словари как деревья, я хотел:
a
так, чтобы каждый путь к каждому листуb
вa
a
если лист найден в соответствующем пути вb
b
листовые узлы остаются листами.Существующие ответы были немного сложными на мой вкус и оставили некоторые детали на полке. Я собрал следующее, которое проходит модульные тесты для моего набора данных.
Пример (отформатирован для ясности):
Пути,
b
которые необходимо было сохранить, были:1 -> 'b' -> 'white'
2 -> 'd' -> 'black'
3 -> 'e'
,a
имел уникальные и неконфликтные пути:1 -> 'a' -> 'red'
1 -> 'c' -> 'orange' -> 'dog'
так что они все еще представлены на объединенной карте.
источник
У меня есть итеративное решение - намного лучше работает с большими dicts и многими из них (например, jsons и т. Д.):
обратите внимание, что это будет использовать значение в d2, чтобы переопределить d1, если они не являются обоими dicts. (как у питона
dict.update()
)некоторые тесты:
Я тестировал около 1200 диктовок - этот метод занял 0,4 секунды, в то время как рекурсивное решение заняло ~ 2,5 секунды.
источник
Это должно помочь в объединении всех элементов из
dict2
вdict1
:Пожалуйста, протестируйте его и сообщите нам, что вы хотели.
РЕДАКТИРОВАТЬ:
Вышеупомянутое решение объединяет только один уровень, но правильно решает пример, данный OP. Для объединения нескольких уровней следует использовать рекурсию.
источник
for k,v in dict2.iteritems(): dict1.setdefault(k,{}).update(v)
. Но, как указал @agf, это не объединяет вложенные dicts.{'a':'b'}
с{'a':{'c':'d'}
).Я тестировал ваши решения и решил использовать это в своем проекте:
Передача функций в качестве параметров является ключом к расширению решения jterrace, чтобы оно работало как все другие рекурсивные решения.
источник
Самый простой способ, о котором я могу думать:
Вывод:
источник
У меня есть еще одно немного другое решение:
По умолчанию он разрешает конфликты в пользу значений из второго дикта, но вы можете легко переопределить это, с помощью некоторого колдовства вы можете даже выбросить исключения из него. :).
источник
источник
эй, у меня тоже была такая же проблема, но у меня есть решение, и я опубликую его здесь, на случай, если он также будет полезен для других, в основном слияние вложенных словарей, а также добавление значений, для меня мне нужно было рассчитать некоторые вероятности, поэтому это один отлично работал:
используя вышеуказанный метод, мы можем объединить:
target = {'6,6': {'6,63': 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 1} , '6,63': {'63, 4 ': 1}}
src = {'5,4': {'4,4': 1}, '5,5': {'5,4': 1}, '4,4': {'4,3': 1} }
и это будет: {'5,5': {'5,4': 1}, '5,4': {'4,4': 1}, '6,6': {'6,63' : 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 2},' 6,63 ': {'63, 4': 1 }}
также обратите внимание на изменения здесь:
target = {'6,6': {'6,63': 1}, '6,63': {'63, 4 ': 1}, ' 4,4 ': {' 4,3 ': 1} , '63, 4 ': {' 4,4 ': 1}}
src = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '4,4': {'4,9': 1} , '3,4': {'4,4': 1}, '5,5': {'5,4': 1}}
merge = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '6,63': {'63, 4 ': 1} , '5,5': {'5,4': 1}, '6,6': {'6,63': 1}, '3,4': {'4,4': 1}, ' 63,4 ': {' 4,4 ': 1}, ' 4,4 ': {' 4,3 ': 1,' 4,9 ': 1} }
не забудьте также добавить импорт для копирования:
источник
Вывод:
источник
взгляни на
toolz
пакетдает
источник
Следующая функция объединяет b в a.
источник
И еще одна небольшая вариация:
Вот чистая функция глубокого обновления на основе набора Python3. Он обновляет вложенные словари, проходя по одному уровню за раз и вызывая себя для обновления каждого следующего уровня значений словаря:
Простой пример:
источник
Как насчет другого ответа?!? Этот также позволяет избежать мутации / побочных эффектов:
источник