Aktualisieren Sie den Wert eines verschachtelten Wörterbuchs unterschiedlicher Tiefe


161

Ich suche nach einer Möglichkeit, dict dictionary1 mit dem Inhalt von dict update zu aktualisieren, ohne LevelA zu überschreiben

dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}

Ich weiß, dass das Update die Werte in Level2 löscht, weil es die niedrigste Schlüsselebene1 aktualisiert.

Wie könnte ich das angehen, da Wörterbuch1 und Update beliebig lang sein können?


Ist die Verschachtelung immer drei Ebenen tief oder können Sie eine beliebige Verschachtelungstiefe haben?
ChristopheD

Es kann jede Tiefe / Länge haben.
Jay_t

Korrigieren Sie mich, wenn ich falsch liege, aber es scheint, dass die ideale Lösung hier die Implementierung des zusammengesetzten Entwurfsmusters erfordert.
Alexander McNulty

Antworten:


262

Die Antwort von @ FM hat die richtige allgemeine Idee, dh eine rekursive Lösung, aber etwas eigenartige Codierung und mindestens einen Fehler. Ich würde stattdessen empfehlen:

Python 2:

import collections

def update(d, u):
    for k, v in u.iteritems():
        if isinstance(v, collections.Mapping):
            d[k] = update(d.get(k, {}), v)
        else:
            d[k] = v
    return d

Python 3:

import collections.abc

def update(d, u):
    for k, v in u.items():
        if isinstance(v, collections.abc.Mapping):
            d[k] = update(d.get(k, {}), v)
        else:
            d[k] = v
    return d

Der Fehler zeigt sich , wenn die „update“ hat einen k, vPunkt , wo va dictund kist nicht ursprünglich ein Schlüssel im Wörterbuch aktualisiert - @ FM Code „überspringt“ dieser Teil des Updates (weil er es auf einen leeren neuen führt dictdie wird nirgendwo gespeichert oder zurückgegeben, sondern geht nur verloren, wenn der rekursive Aufruf zurückkehrt.

Meine anderen Änderungen sind geringfügig: Es gibt keinen Grund für das Konstrukt if/ else, wenn .getderselbe Job schneller und sauberer ausgeführt wird, und es wird aus Gründen der isinstanceAllgemeinheit am besten auf abstrakte Basisklassen (keine konkreten) angewendet.


7
+1 Guter Fang für den Bug - doh! Ich dachte, jemand hätte eine bessere Möglichkeit, mit dem isinstanceTest umzugehen, dachte aber, ich würde es versuchen.
FMc

6
Ein anderes kleines "Merkmal" bewirkt, dass dies ausgelöst wird, TypeError: 'int' object does not support item assignment.wenn Sie z update({'k1': 1}, {'k1': {'k2': 2}}). Um dieses Verhalten zu ändern und stattdessen die Tiefe der Wörterbücher zu erweitern, um Platz für tiefere Wörterbücher zu schaffen, können Sie ein elif isinstance(d, Mapping):um d[k] = u[k]und nach der isinstanceBedingung hinzufügen . Sie müssen auch ein hinzufügen else: d = {k: u[k]}, um den Fall zu behandeln, dass das Aktualisierungsdiktat tiefer liegt als das ursprüngliche Diktat. Gerne bearbeiten wir die Antwort, möchten aber keinen prägnanten Code verschmutzen, der das Problem des OP löst.
Kochfelder

1
Warum isinstance(v, collections.Mapping)eher verwenden als isinstance(v, dict)? Für den Fall, dass OP beschließt, Sammlungen zu verwenden?
Matt

2
@Matt Yea oder ein anderes vom Mapping abgeleitetes Objekt (Listen von Dingenpaaren). Die Funktion wird allgemeiner und es ist weniger wahrscheinlich, dass von Mappings abgeleitete Objekte stillschweigend ignoriert und nicht aktualisiert werden (heimtückischer Fehler, den das OP möglicherweise nie sieht / fängt). Sie möchten fast immer Mapping verwenden, um Dikt-Typen zu finden, und Basestring, um Str-Typen zu finden.
Kochfelder

2
Wenn Sie dies unter Python 3+ ausführen, ändern Sie u.iteritems()auf u.items(), sonst werden Sie feststellen:AttributeError: 'dict' object has no attribute 'iteritems'
Greg K

23

Ich habe ein bisschen gebraucht, aber dank @ Alex 'Post hat er die Lücke gefüllt, die mir gefehlt hat. Ich bin jedoch auf ein Problem gestoßen, wenn ein Wert innerhalb des Rekursiven dictzufällig ein ist list, und ich dachte, ich würde ihn teilen und seine Antwort erweitern.

import collections

def update(orig_dict, new_dict):
    for key, val in new_dict.iteritems():
        if isinstance(val, collections.Mapping):
            tmp = update(orig_dict.get(key, { }), val)
            orig_dict[key] = tmp
        elif isinstance(val, list):
            orig_dict[key] = (orig_dict.get(key, []) + val)
        else:
            orig_dict[key] = new_dict[key]
    return orig_dict

3
Ich denke das sollte wohl sein (um ein bisschen sicherer zu sein) : orig_dict.get(key, []) + val.
Andy Hayden

2
Da Diktate veränderlich sind, ändern Sie die Instanz, die Sie als Argument übergeben. Dann müssen Sie orig_dict nicht zurückgeben.
gabrielhpugliese

3
Ich denke, die meisten Leute würden erwarten, dass die Definition das aktualisierte Diktat zurückgibt, obwohl es an Ort und Stelle aktualisiert wird.
Kel Solaar

Die Standardlogik im Onosendi-Code besteht darin, die aktualisierte Liste an die ursprüngliche Liste anzuhängen. Wenn Sie ein Update benötigen, um die ursprüngliche Liste zu überschreiben, müssen Sie orig_dict [key] = val
intijk

1
@gabrielhpugliese Rückgabe des Originals ist erforderlich, wenn mit einem Wörterbuch Literal aufgerufen, zB merged_tree = update({'default': {'initialvalue': 1}}, other_tree)
EoghanM

18

Die Antwort von @ Alex ist gut, funktioniert aber nicht, wenn ein Element wie eine Ganzzahl durch ein Wörterbuch wie z update({'foo':0},{'foo':{'bar':1}}). Dieses Update behebt es:

import collections
def update(d, u):
    for k, v in u.iteritems():
        if isinstance(d, collections.Mapping):
            if isinstance(v, collections.Mapping):
                r = update(d.get(k, {}), v)
                d[k] = r
            else:
                d[k] = u[k]
        else:
            d = {k: u[k]}
    return d

update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})

Aha. Sie haben meine elifÜberprüfung des ursprünglichen Objekttyps zu einer "einschließenden" Bedingung gemacht, die die Überprüfung sowohl des Werts als auch des Schlüssels dieses Diktats / dieser Zuordnung enthält. Klug.
Kochfelder

Dies funktioniert nicht, wenn das innere Diktat mehr als einen Schlüssel hat.
Wlerin

@Wlerin, es funktioniert immer noch; Bis dahin ist d zu einem Mapping geworden. Hier ist ein Testfall mit mehreren Schlüsseln : update({'A1': 1, 'A2':2}, {'A1': {'B1': {'C1': 3, 'C2':4}, 'B2':2}, 'A3':5}). Haben Sie ein Beispiel, das nicht das tut, was Sie wollen?
bscan

Warum if isinstance(d, collections.Mapping)bei jeder Iteration testen ? Siehe meine Antwort .
Jérôme

13

Dieselbe Lösung wie die akzeptierte, aber klarere Benennung der Variablen, Dokumentzeichenfolge und Behebung eines Fehlers, bei {}dem ein Wert nicht überschrieben wird.

import collections


def deep_update(source, overrides):
    """
    Update a nested dictionary or similar mapping.
    Modify ``source`` in place.
    """
    for key, value in overrides.iteritems():
        if isinstance(value, collections.Mapping) and value:
            returned = deep_update(source.get(key, {}), value)
            source[key] = returned
        else:
            source[key] = overrides[key]
    return source

Hier einige Testfälle:

def test_deep_update():
    source = {'hello1': 1}
    overrides = {'hello2': 2}
    deep_update(source, overrides)
    assert source == {'hello1': 1, 'hello2': 2}

    source = {'hello': 'to_override'}
    overrides = {'hello': 'over'}
    deep_update(source, overrides)
    assert source == {'hello': 'over'}

    source = {'hello': {'value': 'to_override', 'no_change': 1}}
    overrides = {'hello': {'value': 'over'}}
    deep_update(source, overrides)
    assert source == {'hello': {'value': 'over', 'no_change': 1}}

    source = {'hello': {'value': 'to_override', 'no_change': 1}}
    overrides = {'hello': {'value': {}}}
    deep_update(source, overrides)
    assert source == {'hello': {'value': {}, 'no_change': 1}}

    source = {'hello': {'value': {}, 'no_change': 1}}
    overrides = {'hello': {'value': 2}}
    deep_update(source, overrides)
    assert source == {'hello': {'value': 2, 'no_change': 1}}

Diese Funktion ist im charlatan- Paket in verfügbar charlatan.utils.


7

Hier ist eine unveränderliche Version der rekursiven Wörterbuchzusammenführung, falls jemand sie benötigt.

Basierend auf der Antwort von @Alex Martelli .

Python 2.x:

import collections
from copy import deepcopy


def merge(dict1, dict2):
    ''' Return a new dictionary by merging two dictionaries recursively. '''

    result = deepcopy(dict1)

    for key, value in dict2.iteritems():
        if isinstance(value, collections.Mapping):
            result[key] = merge(result.get(key, {}), value)
        else:
            result[key] = deepcopy(dict2[key])

    return result

Python 3.x:

import collections
from copy import deepcopy


def merge(dict1, dict2):
    ''' Return a new dictionary by merging two dictionaries recursively. '''

    result = deepcopy(dict1)

    for key, value in dict2.items():
        if isinstance(value, collections.Mapping):
            result[key] = merge(result.get(key, {}), value)
        else:
            result[key] = deepcopy(dict2[key])

    return result

6

Kleinere Verbesserungen an der Antwort von @ Alex, die das Aktualisieren von Wörterbüchern unterschiedlicher Tiefe sowie das Begrenzen der Tiefe ermöglicht, in die das Update in das ursprünglich verschachtelte Wörterbuch eintaucht (die Aktualisierungstiefe des Wörterbuchs ist jedoch nicht begrenzt). Es wurden nur wenige Fälle getestet:

def update(d, u, depth=-1):
    """
    Recursively merge or update dict-like objects. 
    >>> update({'k1': {'k2': 2}}, {'k1': {'k2': {'k3': 3}}, 'k4': 4})
    {'k1': {'k2': {'k3': 3}}, 'k4': 4}
    """

    for k, v in u.iteritems():
        if isinstance(v, Mapping) and not depth == 0:
            r = update(d.get(k, {}), v, depth=max(depth - 1, -1))
            d[k] = r
        elif isinstance(d, Mapping):
            d[k] = u[k]
        else:
            d = {k: u[k]}
    return d

1
Danke dafür! Für welchen Anwendungsfall könnte der Tiefenparameter gelten?
Matt

@Matt, wenn Sie einige Objekte / Diktate in einer bekannten Tiefe haben, die nicht zusammengeführt / aktualisiert werden sollen, sondern nur mit neuen Objekten überschrieben werden (z. B. Ersetzen eines Diktats durch einen String oder Float oder was auch immer, tief in Ihrem Diktat)
Kochfelder

1
Dies funktioniert nur, wenn das Update höchstens 1 Ebene tiefer als das Original ist. Zum Beispiel schlägt dies fehl: update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})Ich habe eine Antwort hinzugefügt, die dies anspricht
bscan

@bscan guter Fang! Ich habe nie an diesen Anwendungsfall gedacht. Ich denke, ich sollte tiefer in die Elfenzweige zurückkehren. Irgendwelche Ideen?
Kochfelder

Warum if isinstance(d, Mapping)bei jeder Iteration testen ? Siehe meine Antwort . (Ich bin mir auch nicht sicher d = {k: u[k]})
Jérôme

4

Diese Frage ist alt, aber ich bin hier gelandet, als ich nach einer "Deep Merge" -Lösung gesucht habe. Die obigen Antworten haben das Folgende inspiriert. Am Ende habe ich meine eigenen geschrieben, weil es in allen von mir getesteten Versionen Fehler gab. Der kritische Punkt, der übersehen wurde, war, dass in einer beliebigen Tiefe der beiden Eingabediktate für einen Schlüssel k der Entscheidungsbaum, wenn d [k] oder u [k] kein Diktat ist, fehlerhaft war.

Außerdem erfordert diese Lösung keine Rekursion, die symmetrischer zur Funktionsweise dict.update()ist, und kehrt zurück None.

import collections
def deep_merge(d, u):
   """Do a deep merge of one dict into another.

   This will update d with values in u, but will not delete keys in d
   not found in u at some arbitrary depth of d. That is, u is deeply
   merged into d.

   Args -
     d, u: dicts

   Note: this is destructive to d, but not u.

   Returns: None
   """
   stack = [(d,u)]
   while stack:
      d,u = stack.pop(0)
      for k,v in u.items():
         if not isinstance(v, collections.Mapping):
            # u[k] is not a dict, nothing to merge, so just set it,
            # regardless if d[k] *was* a dict
            d[k] = v
         else:
            # note: u[k] is a dict

            # get d[k], defaulting to a dict, if it doesn't previously
            # exist
            dv = d.setdefault(k, {})

            if not isinstance(dv, collections.Mapping):
               # d[k] is not a dict, so just set it to u[k],
               # overriding whatever it was
               d[k] = v
            else:
               # both d[k] and u[k] are dicts, push them on the stack
               # to merge
               stack.append((dv, v))

4

Verwenden Sie einfach python-benedict (ich habe es getan) , es hat eine merge(Deepupdate-) Dienstprogrammmethode und viele andere. Es funktioniert mit Python 2 / Python 3 und ist gut getestet.

from benedict import benedict

dictionary1=benedict({'level1':{'level2':{'levelA':0,'levelB':1}}})
update={'level1':{'level2':{'levelB':10}}}
dictionary1.merge(update)
print(dictionary1)
# >> {'level1':{'level2':{'levelA':0,'levelB':10}}}

Installation: pip install python-benedict

Dokumentation: https://github.com/fabiocaccamo/python-benedict


2

In keiner dieser Antworten scheinen die Autoren das Konzept zu verstehen, ein in einem Wörterbuch gespeichertes Objekt zu aktualisieren oder sogar über Wörterbuchelemente zu iterieren (im Gegensatz zu Schlüsseln). Also musste ich eine schreiben, die keine sinnlosen tautologischen Wörterbuchspeicher und -abrufe macht. Es wird angenommen, dass die Diktate andere Diktate oder einfache Typen speichern.

def update_nested_dict(d, other):
    for k, v in other.items():
        if isinstance(v, collections.Mapping):
            d_v = d.get(k)
            if isinstance(d_v, collections.Mapping):
                update_nested_dict(d_v, v)
            else:
                d[k] = v.copy()
        else:
            d[k] = v

Oder noch einfacher, wenn man mit einem beliebigen Typ arbeitet:

def update_nested_dict(d, other):
    for k, v in other.items():
        d_v = d.get(k)
        if isinstance(v, collections.Mapping) and isinstance(d_v, collections.Mapping):
            update_nested_dict(d_v, v)
        else:
            d[k] = deepcopy(v) # or d[k] = v if you know what you're doing

2

Aktualisieren Sie die Antwort von @Alex Martelli, um einen Fehler in seinem Code zu beheben und die Lösung robuster zu machen:

def update_dict(d, u):
    for k, v in u.items():
        if isinstance(v, collections.Mapping):
            default = v.copy()
            default.clear()
            r = update_dict(d.get(k, default), v)
            d[k] = r
        else:
            d[k] = v
    return d

Der Schlüssel ist, dass wir bei der Rekursion oft denselben Typ erstellen möchten , also verwenden wir hier v.copy().clear()aber nicht {}. Und dies ist besonders nützlich, wenn das dicthier vom Typ collections.defaultdictist, der verschiedene Arten von default_factorys haben kann.

Beachten Sie auch, dass das u.iteritems()in in geändert u.items()wurde Python3.


2

Ich habe die von @Alex Martelli vorgeschlagene Lösung verwendet, aber sie schlägt fehl

TypeError 'bool' object does not support item assignment

wenn sich die beiden Wörterbücher auf einer bestimmten Ebene im Datentyp unterscheiden.

Wenn auf derselben Ebene das Element des Wörterbuchs dnur ein Skalar ist (dh Bool), während das Element des Wörterbuchs unoch ein Wörterbuch ist, schlägt die Neuzuweisung fehl, da keine Wörterbuchzuweisung in den Skalar (wie True[k]) möglich ist.

Eine zusätzliche Bedingung behebt Folgendes:

from collections import Mapping

def update_deep(d, u):
    for k, v in u.items():
        # this condition handles the problem
        if not isinstance(d, Mapping):
            d = u
        elif isinstance(v, Mapping):
            r = update_deep(d.get(k, {}), v)
            d[k] = r
        else:
            d[k] = u[k]

    return d

2

Der folgende Code sollte das update({'k1': 1}, {'k1': {'k2': 2}})Problem in der Antwort von @Alex Martelli richtig lösen .

def deepupdate(original, update):
    """Recursively update a dict.

    Subdict's won't be overwritten but also updated.
    """
    if not isinstance(original, abc.Mapping):
        return update
    for key, value in update.items():
        if isinstance(value, abc.Mapping):
            original[key] = deepupdate(original.get(key, {}), value)
        else:
            original[key] = value
    return original

1
def update(value, nvalue):
    if not isinstance(value, dict) or not isinstance(nvalue, dict):
        return nvalue
    for k, v in nvalue.items():
        value.setdefault(k, dict())
        if isinstance(v, dict):
            v = update(value[k], v)
        value[k] = v
    return value

benutze dictodercollections.Mapping


1

Ich weiß, dass diese Frage ziemlich alt ist, poste aber immer noch, was ich mache, wenn ich ein verschachteltes Wörterbuch aktualisieren muss. Wir können die Tatsache verwenden, dass Diktate in Python als Referenz übergeben werden. Angenommen, der Pfad des Schlüssels ist bekannt und durch Punkte getrennt. Forex, wenn wir ein Diktat mit dem Namen data haben:

{
"log_config_worker": {
    "version": 1, 
    "root": {
        "handlers": [
            "queue"
        ], 
        "level": "DEBUG"
    }, 
    "disable_existing_loggers": true, 
    "handlers": {
        "queue": {
            "queue": null, 
            "class": "myclass1.QueueHandler"
        }
    }
}, 
"number_of_archived_logs": 15, 
"log_max_size": "300M", 
"cron_job_dir": "/etc/cron.hourly/", 
"logs_dir": "/var/log/patternex/", 
"log_rotate_dir": "/etc/logrotate.d/"
}

Und wir wollen die Warteschlangenklasse aktualisieren, der Pfad des Schlüssels wäre - log_config_worker.handlers.queue.class

Wir können die folgende Funktion verwenden, um den Wert zu aktualisieren:

def get_updated_dict(obj, path, value):
    key_list = path.split(".")

    for k in key_list[:-1]:
        obj = obj[k]

    obj[key_list[-1]] = value

get_updated_dict(data, "log_config_worker.handlers.queue.class", "myclass2.QueueHandler")

Dies würde das Wörterbuch korrekt aktualisieren.


1

Es könnte sein, dass Sie über ein Nicht-Standard-Wörterbuch wie mich heute stolpern, das kein iteritems-Attribut hat. In diesem Fall ist es einfach, diese Art von Wörterbuch als Standardwörterbuch zu interpretieren. Beispiel: Python 2.7:

    import collections
    def update(orig_dict, new_dict):
        for key, val in dict(new_dict).iteritems():
            if isinstance(val, collections.Mapping):
                tmp = update(orig_dict.get(key, { }), val)
                orig_dict[key] = tmp
            elif isinstance(val, list):
                orig_dict[key] = (orig_dict[key] + val)
            else:
                orig_dict[key] = new_dict[key]
        return orig_dict

    import multiprocessing
    d=multiprocessing.Manager().dict({'sample':'data'})
    u={'other': 1234}

    x=update(d, u)
    x.items()

Python 3.8:

    def update(orig_dict, new_dict):
        orig_dict=dict(orig_dict)
        for key, val in dict(new_dict).items():
            if isinstance(val, collections.abc.Mapping):
                tmp = update(orig_dict.get(key, { }), val)
                orig_dict[key] = tmp
            elif isinstance(val, list):
                orig_dict[key] = (orig_dict[key] + val)
            else:
                orig_dict[key] = new_dict[key]
        return orig_dict

    import collections
    import multiprocessing
    d=multiprocessing.Manager().dict({'sample':'data'})
    u={'other': 1234, "deeper": {'very': 'deep'}}

    x=update(d, u)
    x.items()

0

Ja! Und noch eine Lösung. Meine Lösung unterscheidet sich in den Schlüsseln, die überprüft werden. Bei allen anderen Lösungen sehen wir uns nur die Schlüssel andict_b . Aber hier schauen wir in die Vereinigung beider Wörterbücher.

Mach damit, wie du willst

def update_nested(dict_a, dict_b):
    set_keys = set(dict_a.keys()).union(set(dict_b.keys()))
    for k in set_keys:
        v = dict_a.get(k)
        if isinstance(v, dict):
            new_dict = dict_b.get(k, None)
            if new_dict:
                update_nested(v, new_dict)
        else:
            new_value = dict_b.get(k, None)
            if new_value:
                dict_a[k] = new_value

0

Wenn Sie ein "vollständig verschachteltes Wörterbuch durch Arrays" ersetzen möchten, können Sie dieses Snippet verwenden:

Es wird jeden "alten_Wert" durch "neuen_Wert" ersetzen. Es wird ungefähr eine tiefgreifende Neuerstellung des Wörterbuchs durchgeführt. Es kann sogar mit List oder Str / int als Eingabeparameter der ersten Ebene arbeiten.

def update_values_dict(original_dict, future_dict, old_value, new_value):
    # Recursively updates values of a nested dict by performing recursive calls

    if isinstance(original_dict, Dict):
        # It's a dict
        tmp_dict = {}
        for key, value in original_dict.items():
            tmp_dict[key] = update_values_dict(value, future_dict, old_value, new_value)
        return tmp_dict
    elif isinstance(original_dict, List):
        # It's a List
        tmp_list = []
        for i in original_dict:
            tmp_list.append(update_values_dict(i, future_dict, old_value, new_value))
        return tmp_list
    else:
        # It's not a dict, maybe a int, a string, etc.
        return original_dict if original_dict != old_value else new_value

0

Eine andere Möglichkeit, die Rekursion zu verwenden:

def updateDict(dict1,dict2):
    keys1 = list(dict1.keys())
    keys2= list(dict2.keys())
    keys2 = [x for x in keys2 if x in keys1]
    for x in keys2:
        if (x in keys1) & (type(dict1[x]) is dict) & (type(dict2[x]) is dict):
            updateDict(dict1[x],dict2[x])
        else:
            dict1.update({x:dict2[x]})
    return(dict1)

0

ein neues Q wie man durch eine Schlüsselkette

dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}},'anotherLevel1':{'anotherLevel2':{'anotherLevelA':0,'anotherLevelB':1}}}
update={'anotherLevel1':{'anotherLevel2':1014}}
dictionary1.update(update)
print dictionary1
{'level1':{'level2':{'levelA':0,'levelB':1}},'anotherLevel1':{'anotherLevel2':1014}}

0

Sie könnten dies versuchen, es funktioniert mit Listen und ist rein:

def update_keys(newd, dic, mapping):
  def upsingle(d,k,v):
    if k in mapping:
      d[mapping[k]] = v
    else:
      d[k] = v
  for ekey, evalue in dic.items():
    upsingle(newd, ekey, evalue)
    if type(evalue) is dict:
      update_keys(newd, evalue, mapping)
    if type(evalue) is list:
      upsingle(newd, ekey, [update_keys({}, i, mapping) for i in evalue])
  return newd

0

Ich empfehle, {}durch zu ersetzen type(v)(), um den Objekttyp einer Dikt-Unterklasse zu verbreiten, die in gespeichert ist, uaber nicht vorhanden ist d. Dies würde beispielsweise Typen wie Sammlungen beibehalten. OrderedDict:

Python 2:

import collections

def update(d, u):
    for k, v in u.iteritems():
        if isinstance(v, collections.Mapping):
            d[k] = update(d.get(k, type(v)()), v)
        else:
            d[k] = v
    return d

Python 3:

import collections.abc

def update(d, u):
    for k, v in u.items():
        if isinstance(v, collections.abc.Mapping):
            d[k] = update(d.get(k, type(v)()), v)
        else:
            d[k] = v
    return d

-1

Das ist ein bisschen zur Seite, aber brauchen Sie wirklich verschachtelte Wörterbücher? Je nach Problem kann manchmal ein flaches Wörterbuch ausreichen ... und gut aussehen:

>>> dict1 = {('level1','level2','levelA'): 0}
>>> dict1['level1','level2','levelB'] = 1
>>> update = {('level1','level2','levelB'): 10}
>>> dict1.update(update)
>>> print dict1
{('level1', 'level2', 'levelB'): 10, ('level1', 'level2', 'levelA'): 0}

5
Die verschachtelte Struktur stammt aus eingehenden JSON-Datensätzen, daher möchte ich sie intakt halten, ...
jay_t

-1

Wenn Sie einen Einzeiler wollen:

{**dictionary1, **{'level1':{**dictionary1['level1'], **{'level2':{**dictionary1['level1']['level2'], **{'levelB':10}}}}}}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.