Szukam sposobu na zaktualizowanie słownika Dict Dictionary1 z zawartością aktualizacji dict bez nadpisywania poziomu A.
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}
Wiem, że aktualizacja usuwa wartości na poziomie 2, ponieważ aktualizuje najniższy klucz poziom 1.
Jak mogę sobie z tym poradzić, biorąc pod uwagę, że słownik1 i aktualizacja mogą mieć dowolną długość?
Odpowiedzi:
Odpowiedź @ FM ma właściwą ogólną ideę, tj. Rozwiązanie rekurencyjne, ale nieco osobliwe kodowanie i przynajmniej jeden błąd. Zamiast tego polecam:
Python 2:
Python 3:
Bug pojawia się, gdy „zmiana” ma
k
,v
element gdziev
jestdict
ik
nie jest pierwotnie kluczowy w słowniku aktualizowany - @ kodem „przeskakuje” FM za tę część aktualizacji (bo wykonuje go na pusty nowydict
która nie jest zapisywany ani nigdzie zwracany, po prostu utracony po powrocie wywołania rekurencyjnego).Moje inne zmiany są drobne: nie ma powodu, aby
if
/else
construct wykonywała.get
tę samą pracę szybciej i czyściej, iisinstance
najlepiej jest stosować ją do abstrakcyjnych klas podstawowych (nie konkretnych) dla uogólnienia.źródło
isinstance
testu, ale pomyślałem, że się nim zajrzę.TypeError: 'int' object does not support item assignment.
gdy npupdate({'k1': 1}, {'k1': {'k2': 2}})
. Aby zmienić to zachowanie i zamiast tego rozszerzyć zakres słowników, aby zrobić miejsce na głębsze słowniki, możesz dodać znakelif isinstance(d, Mapping):
wokół warunkud[k] = u[k]
i po nimisinstance
. Będziesz także musiał dodać,else: d = {k: u[k]}
aby poradzić sobie w przypadku, gdy dyktowanie aktualizacji jest głębsze niż oryginalny dykt. Chętnie edytuję odpowiedź, ale nie chcę brudzić zwięzłego kodu, który rozwiązuje problem OP.isinstance(v, collections.Mapping)
zamiastisinstance(v, dict)
? W przypadku, gdy OP zdecyduje się rozpocząć korzystanie z kolekcji?u.iteritems()
nau.items()
, w przeciwnym razie napotkasz:AttributeError: 'dict' object has no attribute 'iteritems'
Trochę mi to zajęło, ale dzięki postowi @ Alexa wypełnił lukę, której mi brakowało. Jednak napotkałem problem, jeśli
dict
zdarza sięlist
, że wartość w rekursywnym jest a , więc pomyślałem, że podzielę się i rozszerzę jego odpowiedź.źródło
orig_dict.get(key, []) + val
.merged_tree = update({'default': {'initialvalue': 1}}, other_tree)
@ Alex odpowiedź jest dobra, ale nie działa w przypadku zamiany elementu, takiego jak liczba całkowita, na słownik, na przykład
update({'foo':0},{'foo':{'bar':1}})
. Ta aktualizacja dotyczy tego:źródło
elif
czek z oryginalnego obiektu wpisać „zamykając” warunkowe zawierające kontrole zarówno wartości i klucz tego dict / mapowania. Sprytny.update({'A1': 1, 'A2':2}, {'A1': {'B1': {'C1': 3, 'C2':4}, 'B2':2}, 'A3':5})
. Czy masz przykład, który nie robi tego, co chcesz?if isinstance(d, collections.Mapping)
w każdej iteracji? Zobacz moją odpowiedź .Takie samo rozwiązanie jak przyjęte, ale jaśniejsze nazewnictwo zmiennych, napisy dokumentacyjne i naprawiono błąd, w którym
{}
wartość nie nadpisywała.Oto kilka przypadków testowych:
Ta funkcja jest dostępna w pakiecie szarlatan w formacie
charlatan.utils
.źródło
Oto niezmienna wersja rekurencyjnego scalania słowników na wypadek, gdyby ktoś tego potrzebował.
W oparciu @Alex martelli za odpowiedź .
Python 2.x:
Python 3.x:
źródło
Niewielkie ulepszenia odpowiedzi @ Alex, która umożliwia aktualizację słowników o różnych głębokościach, a także ogranicza głębokość, na jaką aktualizacja zagłębia się w oryginalnym zagnieżdżonym słowniku (ale głębokość aktualizacji słownika nie jest ograniczona). Przetestowano tylko kilka przypadków:
źródło
update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})
dodałem odpowiedź, która rozwiązuje tenif isinstance(d, Mapping)
w każdej iteracji? Zobacz moją odpowiedź . (Również nie jestem pewien co do twojegod = {k: u[k]}
)To pytanie jest stare, ale trafiłem tutaj, szukając rozwiązania „głębokiego scalania”. Powyższe odpowiedzi zainspirowały poniższe. Skończyło się na tym, że napisałem własną, ponieważ we wszystkich testowanych wersjach były błędy. Punktem krytycznym pominiętym było, na jakiejś arbitralnej głębokości dwóch dykt wejściowych, dla jakiegoś klucza k, drzewo decyzyjne, gdy d [k] lub u [k] nie jest dyktem, było błędne.
Ponadto to rozwiązanie nie wymaga rekursji, która jest bardziej symetryczna z tym
dict.update()
, jak działa, i zwracaNone
.źródło
Po prostu użyj
python-benedict
(zrobiłem to) , mamerge
(głęboką aktualizację) metodę narzędziową i wiele innych. Działa z pythonem 2 / pythonem 3 i jest dobrze przetestowany.Instalacja:
pip install python-benedict
Dokumentacja: https://github.com/fabiocaccamo/python-benedict
źródło
W żadnej z tych odpowiedzi autorzy nie wydają się rozumieć koncepcji aktualizacji obiektu przechowywanego w słowniku ani nawet iteracji po elementach słownika (w przeciwieństwie do kluczy). Musiałem więc napisać taki, który nie prowadzi do zbędnych magazynów i wyszukiwania w słowniku tautologicznym. Zakłada się, że dykty przechowują inne dykty lub proste typy.
Lub jeszcze prostszy pracujący z dowolnym typem:
źródło
Zaktualizuj odpowiedź @Alex Martelli, aby naprawić błąd w jego kodzie, aby rozwiązanie było bardziej niezawodne:
Kluczem jest to, że często chcemy utworzyć ten sam typ przy rekurencji, więc tutaj używamy,
v.copy().clear()
ale nie{}
. Jest to szczególnie przydatne, jeślidict
tutaj jest typ,collections.defaultdict
który może mieć różne rodzajedefault_factory
.Zwróć też uwagę, że
u.iteritems()
zmieniono nau.items()
wPython3
.źródło
Skorzystałem z rozwiązania, które sugeruje @Alex Martelli, ale zawodzi
TypeError 'bool' object does not support item assignment
kiedy dwa słowniki różnią się na pewnym poziomie typem danych.
W przypadku, gdy na tym samym poziomie element słownika
d
jest po prostu skalarem (tj.Bool
), Podczas gdy element słownikau
jest nadal słownikiem, ponowne przypisanie nie powiedzie się, ponieważ nie jest możliwe przypisanie słownika do skalarnego (podobnieTrue[k]
).Jeden dodatkowy warunek rozwiązuje ten problem:
źródło
Poniższy kod powinien rozwiązać
update({'k1': 1}, {'k1': {'k2': 2}})
problem w odpowiedzi @Alex Martelli we właściwy sposób.źródło
użyj
dict
lubcollections.Mapping
źródło
Wiem, że to pytanie jest dość stare, ale nadal publikuję, co robię, gdy muszę zaktualizować zagnieżdżony słownik. Możemy wykorzystać fakt, że dykty są przekazywane przez referencje w pythonie Zakładając, że ścieżka klucza jest znana i jest oddzielona kropkami. Forex jeśli mamy dykt o nazwie dane:
Chcemy zaktualizować klasę kolejki, ścieżka klucza będzie wyglądać następująco:
log_config_worker.handlers.queue.class
Aby zaktualizować wartość, możemy skorzystać z następującej funkcji:
Spowoduje to poprawną aktualizację słownika.
źródło
Może się zdarzyć, że natkniesz się na niestandardowy słownik, taki jak ja dzisiaj, który nie ma iteritems-Attribute. W takim przypadku łatwo jest zinterpretować ten typ słownika jako słownik standardowy. Np .: Python 2.7:
Python 3.8:
źródło
Tak! I inne rozwiązanie. Moje rozwiązanie różni się kluczami, które są sprawdzane. We wszystkich innych rozwiązaniach patrzymy tylko na klucze
dict_b
. Ale tutaj patrzymy na połączenie obu słowników.Zrób to, co chcesz
źródło
Jeśli chcesz zamienić „pełny słownik zagnieżdżony na tablice”, możesz użyć tego fragmentu:
Zastąpi on każdą „starą wartość” przez „nową_wartość”. Z grubsza polega na przebudowie słownika w pierwszej kolejności. Może nawet działać z listą lub str / int podanym jako parametr wejściowy pierwszego poziomu.
źródło
Inny sposób wykorzystania rekurencji:
źródło
nowy Q jak przez breloczek do kluczy
źródło
możesz spróbować tego, działa z listami i jest czysty:
źródło
Zalecam zastąpienie
{}
przeztype(v)()
, aby propagować typ obiektu dowolnej podklasy dict, która jest przechowywana,u
ale której nie mad
. Na przykład zachowałoby to typy takie jak kolekcje.Python 2:
Python 3:
źródło
To trochę na marginesie, ale czy naprawdę potrzebujesz zagnieżdżonych słowników? W zależności od problemu czasami może wystarczyć płaski słownik ... i spójrz na to dobrze:
źródło
Jeśli chcesz mieć jedną linijkę:
źródło