Я ищу способ обновить dict dictionary1 содержимым dict update без перезаписи уровняA
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}
Я знаю, что обновление удаляет значения в level2, потому что оно обновляет самый низкий ключ level1.
Как я могу решить это, учитывая, что dictionary1 и обновление могут иметь любую длину?
Ответы:
Ответ @ FM имеет правильную общую идею, то есть рекурсивное решение, но несколько своеобразное кодирование и хотя бы одну ошибку. Я бы порекомендовал вместо этого:
Python 2:
Python 3:
Показывает ошибку вверх , когда «обновление» есть
k
,v
пункт , гдеv
являетсяdict
иk
не является изначально ключом в словаре обновляется - @ код «скачет» FM в этой части обновления (поскольку он выполняет его на пустом новом ,dict
который нигде не сохраняется и не возвращается, просто теряется при возврате рекурсивного вызова).Мои другие изменения незначительны: нет никакой причины для конструкции
if
/,else
когда.get
та же самая работа выполняется быстрее и чище, иisinstance
ее лучше всего применять для абстрактных базовых классов (не конкретных) для общности.источник
isinstance
думал, что у кого-то будет лучший способ справиться с тестом, но подумал, что мне нужно сделать на него удар.TypeError: 'int' object does not support item assignment.
когда вы, напримерupdate({'k1': 1}, {'k1': {'k2': 2}})
. Чтобы изменить это поведение и вместо этого расширить глубину словарей, чтобы освободить место для более глубоких словарей, вы можете добавитьelif isinstance(d, Mapping):
вокругd[k] = u[k]
и послеisinstance
условия. Вам также нужно будет добавить,else: d = {k: u[k]}
чтобы иметь дело со случаем, когда обновленный dict глубже, чем исходный. Рад редактировать ответ, но не хочу испачкать краткий код, который решает проблему ОП.isinstance(v, collections.Mapping)
а неisinstance(v, dict)
? В случае, если OP решит начать использовать коллекции?u.iteritems()
наu.items()
, иначе вы встретите:AttributeError: 'dict' object has no attribute 'iteritems'
Взял меня немного на этот, но благодаря посту @ Алекса, он заполнил пробел, который я пропустил. Однако, я столкнулся с проблемой, если значение в рекурсиве
dict
оказалось alist
, поэтому я решил поделиться и расширить его ответ.источник
orig_dict.get(key, []) + val
.merged_tree = update({'default': {'initialvalue': 1}}, other_tree)
@ Ответ Алекса хорош, но не работает при замене элемента, такого как целое число, словарем, например
update({'foo':0},{'foo':{'bar':1}})
. Это обновление исправляет это:источник
elif
проверку исходного типа объекта условным "включающим", содержащим проверки как значения, так и ключа этого dict / mapping. Умная.update({'A1': 1, 'A2':2}, {'A1': {'B1': {'C1': 3, 'C2':4}, 'B2':2}, 'A3':5})
. У вас есть пример, который не делает то, что вы хотите?if isinstance(d, collections.Mapping)
на каждой итерации? Смотри мой ответ .То же решение, что и принятое, но более четкое именование переменных, строка документации и исправлена ошибка, при которой
{}
значение не переопределялось.Вот несколько тестовых случаев:
Эта функция доступна в пакете шарлатан , в
charlatan.utils
.источник
Вот неизменяемая версия рекурсивного слияния словаря на случай, если это кому-нибудь понадобится.
Основано на ответе @Alex Martelli .
Python 2.x:
Python 3.x:
источник
Незначительные улучшения в ответе @ Alex, которые позволяют обновлять словари различной глубины, а также ограничивают глубину, с которой обновление погружается в исходный вложенный словарь (но глубина обновления словаря не ограничена). Только несколько случаев были проверены:
источник
update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})
я добавил ответ, который обращается к этомуif isinstance(d, Mapping)
на каждой итерации? Смотри мой ответ . (Кроме того, я не уверен в вашемd = {k: u[k]}
)Этот вопрос старый, но я попал сюда при поиске решения "глубокого слияния". Ответы выше вдохновили то, что следует. Я написал свою собственную, потому что во всех версиях, которые я тестировал, были ошибки. Пропущенная критическая точка состояла в том, что на некоторой произвольной глубине двух входных диктов для некоторого ключа k было дерево решений, когда d [k] или u [k] не является диктовкой, было ошибочным.
Кроме того, это решение не требует рекурсии, которая более симметрична тому, как
dict.update()
работает, и возвращаетNone
.источник
Просто используйте
python-benedict
(я сделал это) , у него естьmerge
метод (deepupdate) и многие другие. Он работает с python 2 / python 3 и хорошо протестирован.Монтаж:
pip install python-benedict
Документация: https://github.com/fabiocaccamo/python-benedict
источник
Ни в одном из этих ответов авторы, кажется, не понимают концепцию обновления объекта, хранящегося в словаре, и даже итерации по элементам словаря (в отличие от ключей). Так что мне пришлось написать такой, который не делал бы бессмысленные тавтологические словари для хранения и поиска. Предполагается, что дикты хранят другие диктанты или простые типы.
Или даже более простой, работающий с любым типом:
источник
Обновите ответ @Alex Martelli, чтобы исправить ошибку в своем коде, чтобы сделать решение более надежным:
Ключ в том, что мы часто хотим создать один и тот же тип при рекурсии, поэтому здесь мы используем,
v.copy().clear()
но не используем{}
. И это особенно полезно, еслиdict
здесь есть тип,collections.defaultdict
который может иметь различные видыdefault_factory
s.Также обратите внимание, что
u.iteritems()
был изменен наu.items()
вPython3
.источник
Я использовал решение, которое предлагает @Alex Martelli, но оно не помогает
TypeError 'bool' object does not support item assignment
когда два словаря различаются по типу данных на некотором уровне.
В том случае, если на том же уровне элемент словаря
d
является просто скаляром (т. Е.Bool
), В то время как элемент словаряu
все еще является словарем, переназначение не выполняется, так как никакое словарное назначение невозможно в скаляр (например,True[k]
).Одно добавленное условие исправляет это:
источник
Код ниже должен решить
update({'k1': 1}, {'k1': {'k2': 2}})
проблему в ответе @Alex Martelli правильным способом.источник
использовать
dict
илиcollections.Mapping
источник
Я знаю, что этот вопрос довольно старый, но я все еще публикую сообщения о том, что я делаю, когда мне нужно обновить вложенный словарь. Мы можем использовать тот факт, что dicth передаются по ссылке в python. Предполагая, что путь к ключу известен и разделен точками. Форекс, если у нас есть данные с именем dict:
И мы хотим обновить класс очереди, путь к ключу будет -
log_config_worker.handlers.queue.class
Мы можем использовать следующую функцию для обновления значения:
Это обновит словарь правильно.
источник
Возможно, вы наткнулись на нестандартный словарь, такой как я сегодня, у которого нет атрибута iteritems. В этом случае этот тип словаря легко интерпретировать как стандартный словарь. Например: Python 2.7:
Python 3.8:
источник
Да! И еще одно решение. Мое решение отличается ключами, которые проверяются. Во всех других решениях мы рассматриваем только ключи
dict_b
. Но здесь мы смотрим в союз обоих словарей.Делай с этим как хочешь
источник
Если вы хотите заменить «полный вложенный словарь массивами», вы можете использовать этот фрагмент:
Он заменит любое «old_value» на «new_value». Он примерно выполняет глубинную перестройку словаря. Он может даже работать с List или Str / int, заданными в качестве входного параметра первого уровня.
источник
Еще один способ использования рекурсии:
источник
новый вопрос как к цепочке ключей
источник
Вы можете попробовать это, это работает со списками и чисто:
источник
Я рекомендую заменить
{}
наtype(v)()
, чтобы распространить тип объекта любого подкласса dict, хранящегося,u
но отсутствующего вd
. Например, это сохранит типы, такие как collection.OrderedDict:Python 2:
Python 3:
источник
Это немного в стороне, но вам действительно нужны вложенные словари? В зависимости от проблемы, иногда плоского словаря может быть достаточно ... и хорошо выглядеть:
источник
Если вы хотите однострочник:
источник