302 votes

Mise à jour de la valeur d'un dictionnaire imbriqué de profondeur variable

Je cherche un moyen de mettre à jour le dictionnaire 1 avec le contenu du dictionnaire 2 sans écraser le niveau A.

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

Je sais que la mise à jour supprime les valeurs du niveau 2 car elle met à jour la clé la plus basse du niveau 1.

Comment puis-je résoudre ce problème, étant donné que le dictionnaire 1 et la mise à jour peuvent avoir n'importe quelle longueur ?

0 votes

L'imbrication est-elle toujours de trois niveaux de profondeur ou peut-on avoir une imbrication d'une profondeur arbitraire ?

1 votes

Il peut avoir n'importe quelle profondeur/longueur.

0 votes

Corrigez-moi si je me trompe, mais il semble que la solution idéale ici nécessite la mise en œuvre du modèle de conception composite.

3voto

Nico Points 11

Je recommande de remplacer {} par type(v)() afin de propager le type d'objet de toute sous-classe de dict stocké dans u mais absent de d . Par exemple, cela permettrait de préserver des types tels que collections.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

2voto

ipsuri Points 11

Je sais que cette question est assez ancienne, mais je continue à afficher ce que je fais lorsque je dois mettre à jour un dictionnaire imbriqué. Nous pouvons utiliser le fait que les dictionnaires sont passés par référence en Python. En supposant que le chemin de la clé est connu et est séparé par des points. Forex si nous avons un dict nommé data :

{
"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/"
}

Et nous voulons mettre à jour la classe de la file d'attente, le chemin de la clé serait - log_config_worker.handlers.queue.class

Nous pouvons utiliser la fonction suivante pour mettre à jour la valeur :

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")

Cela permettrait de mettre à jour le dictionnaire correctement.

2voto

J'ai créé une fonction simple, dans laquelle vous donnez la clé, la nouvelle valeur et le dictionnaire en entrée, et elle le met à jour récursivement avec la valeur :

def update(key,value,dictionary):
    if key in dictionary.keys():
        dictionary[key] = value
        return
    dic_aux = []
    for val_aux in dictionary.values():
        if isinstance(val_aux,dict):
            dic_aux.append(val_aux)
    for i in dic_aux:
        update(key,value,i)
    for [key2,val_aux2] in dictionary.items():
        if isinstance(val_aux2,dict):
            dictionary[key2] = val_aux2

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

#output: {'level1': {'level2': {'levelA': 0, 'levelB': 10}}}

J'espère que cela répondra à vos attentes.

0voto

ZettaCircl Points 585

Si vous voulez remplacer un "full nested dictionary with arrays", vous pouvez utiliser cet extrait :

Il remplacera toute "ancienne_valeur" par "nouvelle_valeur". Il s'agit en gros de reconstruire le dictionnaire en profondeur. Elle peut même fonctionner avec une liste ou une variable Str/int donnée comme paramètre d'entrée du premier niveau.

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

0voto

zwep Points 522

Oui ! Et une autre solution. Ma solution diffère par les clés qui sont vérifiées. Dans toutes les autres solutions, nous ne regardons que les clés en dict_b . Mais ici nous regardons dans l'union des deux dictionnaires.

Faites-en ce que vous voulez

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X