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?

jay_t
quelle
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.

Alex Martelli
quelle
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
Nate Glenn
quelle
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}}})
bscan
quelle
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.

charlax
quelle
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
Kabirbaidhya
quelle
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
Kochfelder
quelle
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))
djpinne
quelle
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

Fabio Caccamo
quelle
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
Panda-34
quelle
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.

thuzhf
quelle
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
Helvete
quelle
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
Jérôme
quelle
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

honmaple
quelle
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.

ipsuri
quelle
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()
Noragen
quelle
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
zwep
quelle
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
ZettaCircl
quelle
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)
Yifyan
quelle
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}}
user7337353
quelle
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
Craig N.
quelle
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
Nico
quelle
-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}
Nas Banov
quelle
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}}}}}}
Joe '
quelle