IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Récursion et boucles


Sujet :

Python

  1. #1
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut Récursion et boucles
    Bonjour,
    J'essaie de trouver un moyen pour faire une sorte de "deep_update" de dictionnaire par gérer du paramétrage avec surcharge à différent niveau.

    L'idée est que si j'ai un dictionnaire arborescent de paramètres je puisse le compléter par un autre dictionnaire arborescent.
    Je voudrais par ailleurs que cela soit très générique pour me permettre de le réutiliser dans toute sortes de cas.

    Pour décrire l'arborescence du dictionnaire, je fais des trucs du genre (code complet plus bas)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    def deep_update(dico1, dico2):
        for k, v in dico2.items():
            deep_copy(dico1[k], dico2[k])
    Mais ça ne marche pas : seule la première clé de dico 2 est prise en considération. Les boucles ne se font pas.

    Quelqu'un voit-il pourquoi cela ne fonctionne pas ?
    Voici un code complet pour ceux qui voudraient tester le programme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
     
    # -*- coding: iso-8859-1 -*-
    def deep_update(dict_1, dict_2):
        """ Met à jour dict_1 en fonction de dict_2
        """
        # Les procédures employées sont légèrement différentes si les
        # éléments à mettre à jour sont des listes et non des dico
        if type(dict_1) == type([]):
            if type(dict_2) == type([]):
                for key, value in enumerate(dict_2):
                    try:
                        dict_1[key] = deep_update(dict_1[key], value)
                    except IndexError:
                        dict_1.append(value)
                return dict_1
            elif type(dict_2) == type({}):
                # cette ruse est là pour permettre de mettre à jour un élément 
                # précis d'une liste (voir le cas '2' dans l'exemple ci dessous)
                for key, val in dict_2.items():
                    dict_1[int(key)] = deep_update(dict_1[int(key)], value)
                return dict_1
        elif type(dict_1) == type({}):
            for key, value in dict_2.items():
                try:
                    dict_1[key] = deep_update(dict_1[key], value)
                except KeyError:
                    dict_1[key] = value
                return dict_1
        else:
            return dict_2
     
     
    dict_1 = {'1':'string',
              '2':[1, 2, 3, 4],
              '3':{'a':'aaa', 'b':'bbb'}
              }
    dict_2 = {'1':'strong',
              '2':{2:5},
              '3':{'b':'bbbb', 'c':'ccc'},
              '4':'tst'}
     
    print (deep_update(dict_1, dict_2))
    Merci pour votre aide.

  2. #2
    Membre très actif
    Avatar de afranck64
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2009
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Cameroun

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2009
    Messages : 592
    Par défaut
    Bonsoir,

    Je pense que:
    -Tu devrais éviter les "try:...except:..." qui recelent souvent pas mal de problèmes.
    - Le troisième return de ta fonction s'écute au premier pas de la boucle for. D'où...
    Voici ton code un peu retouché.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    def deep_update(dict_1, dict_2):
        """ Met à jour dict_1 en fonction de dict_2
        """
        # Les procédures employées sont légèrement différentes si les
        # éléments à mettre à jour sont des listes et non des dico
        if type(dict_1) == type([]):
            if type(dict_2) == type([]) or type(dict_2) == type(()):
                for key, value in enumerate(dict_2):
                    dict_1[key] = deep_update(dict_1[key], value)
            elif type(dict_2) == type({}):
                # cette ruse est là pour permettre de mettre à jour un élément 
                # précis d'une liste (voir le cas '2' dans l'exemple ci dessous)
                for key, value in dict_2.items():
                    dict_1[int(key)] = deep_update(dict_1[int(key)], value)
            res = dict_1
        elif type(dict_1) == type({}):
            ##On verifie que dict_2 est un dictionnaire
            if type(dict_2)== type({}):
                for key, value in dict_2.items():
                    #on verifie que dict_1 a bien la clef
                    if dict_1.has_key(key):
                        dict_1[key] = deep_update(dict_1[key], value)
                    else:
                        dict_1[key] = deep_update(type(dict_1),value)
            elif type(dict_2) == type([]) or type(dict_2) == type(()):
                for key,value in enumerate(dict_2):
                    dict_1[key] = deep_update(dict_1[key],value)
            res = dict_1
        else:
            res = dict_2
        return res
    Ou en ne modifiant pas le dict_1. Et en utilsant <type> au lieu de type(<type>).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    def deep_update(dict_1, dict_2):
        """ Met à jour dict_1 en fonction de dict_2
        """
        # Les procédures employées sont légèrement différentes si les
        # éléments à mettre à jour sont des listes et non des dico
        if type(dict_1) == list:
            res = list(dict_1)
            if type(dict_2) in (dict,list):
                for key, value in enumerate(dict_2):
                    res[key] = deep_update(dict_1[key], value)
            elif type(dict_2) == type({}):
                # cette ruse est là pour permettre de mettre à jour un élément 
                # précis d'une liste (voir le cas '2' dans l'exemple ci dessous)
                for key, value in dict_2.items():
                    res[int(key)] = deep_update(dict_1[int(key)], value)
        elif type(dict_1) == dict:
            res = dict(dict_1)
            ##On verifie que dict_2 est un dictionnaire
            if type(dict_2)== dict:
                for key, value in dict_2.items():
                    #on verifie que dict_1 a bien la clef
                    if dict_1.has_key(key):
                        res[key] = deep_update(dict_1[key], value)
                    else:
                        res[key] = deep_update(type(dict_1),value)
            elif type(dict_2) in (list,tuple):
                for key,value in enumerate(dict_2):
                    res[key] = deep_update(dict_1[key],value)
        else:
            res = dict_2
        return res

    a plus.
    Win 10 64 bits / Linux Mint 18, - AMD A6 Quad: Py27 / Py35
    CONTENU D'UNE QUESTION
    Exemples:
    - Configuration (système d'exploitation, version de Python et des bibliothèques utilisées)
    - Code source du morceau de programme où il y a un bogue
    - Ligne de code sur laquelle le bogue apparaît
    - Erreur complète retournée pas l'interpréteur Python
    - Recherche déjà effectuée (FAQ, Tutoriels, ...)
    - Tests déjà effectués

  3. #3
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut
    Bonjour,
    Merci pour cette réponse qui fonctionne parfaitement.
    Je n'utilise pas très souvent la récursion et je ne maîtrise pas très bien (et quand je vois ta réponse, finalement pas si complexe, je me sens un peu bêta ;-) )

    Quelques remarques (et/ou questions) cependant.
    - Concernant les try / except.
    J'utilise assez classiquement cette méthode et je n'ai jamais remarqué de problème. Je la trouve même plutôt pas mal dans le sens où elle évite un test : on part du principe que la clé existe et, si ça plante, c'est que la clé n'existe pas (évidemment, cela suppose d'intercepter uniquement l'exception IndexError ou KeyError).
    En utilisant "if key in dict" (ou if dict.has_key(key) mais je crois que has_key devient "deprecated" dans les récentes versions de python), on effectue le test à chaque fois.
    Après, n'ayant pas fait de test de performance, je ne suis pas sûr du gain et, par ailleurs, compte tenu de la taille du dictionnaire traité ici, la différence est certainement très en dessous de ce qui peut être perceptible :-)

    - Concernant le 2ème algo que tu proposes, tu dis qu'il ne modifie pas dict_1 or en fait, il le modifie mais seulement partiellement car une copie de dictionnaire ou de liste renvoie une référence.

    Ainsi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    dict_2 = {'1':'strong',
              '2':{2:5},
              '3':{'b':'bbbb', 'c':'ccc'},
              '4':'tst'}
     
    dict_3 = dict(dict_2)
    dict_3["3"]["c"] = 'cccc'
    dict_3["1"] = 'strange'
     
    print dict_2
    {'1': 'strong', '3': {'c': 'cccc', 'b': 'bbbb'}, '2': {2: 5}, '4': 'tst'}
    print dict_3
    {'1': 'strange', '3': {'c': 'cccc', 'b': 'bbbb'}, '2': {2: 5}, '4': 'tst'}
    On voit que la clé "1" n'a pas été modifiée mais que la ["3"]["c"] l'a été

    Avec ta procédure, on a :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    dict_1 = {'1':'string',
              '2':[1, 2, 3, 4],
              '3':{'a':'aaa', 'b':'bbb'}
              }
    dict_2 = {'1':'strong',
              '2':{2:5},
              '3':{'b':'bbbb', 'c':'ccc'},
              '4':'tst'}
     
    res2 = deep_update_2(dict_1, dict_2)
    print dict_1, res2
    {'1': 'string', '3': {'a': 'aaa', 'c': 'ccc', 'b': 'bbbb'}, '2': [1, 2, 5, 4]}
    {'1': 'strong', '3': {'a': 'aaa', 'c': 'ccc', 'b': 'bbbb'}, '2': [1, 2, 5, 4], '4': 'tst'}
    On voit que '1' n'a pas bougé, que '4' n'a pas été ajouté mais que '2' et '3' on bel et bien changé.

    En tout cas merci pour cette réponse. Couplée à un paramétrage en yaml, le paramétrage de mes applis va roxxer :-)

  4. #4
    Membre très actif
    Avatar de afranck64
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2009
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Cameroun

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2009
    Messages : 592
    Par défaut
    Bonjour,
    De rien.

    try/except...
    C'est vrai qu'explicitement on a pas à faire de tests avec mais je penses que la panoplie de vérications (pour éviter les erreurs, permettre les retours en arrière au cas où,...) dépassent les "if..." éffectués par le programmeur. Mais bon comme tu l'as dit:
    par ailleurs, compte tenu de la taille du dictionnaire traité ici, la différence est certainement très en dessous de ce qui peut être perceptible
    A plus.
    Win 10 64 bits / Linux Mint 18, - AMD A6 Quad: Py27 / Py35
    CONTENU D'UNE QUESTION
    Exemples:
    - Configuration (système d'exploitation, version de Python et des bibliothèques utilisées)
    - Code source du morceau de programme où il y a un bogue
    - Ligne de code sur laquelle le bogue apparaît
    - Erreur complète retournée pas l'interpréteur Python
    - Recherche déjà effectuée (FAQ, Tutoriels, ...)
    - Tests déjà effectués

Discussions similaires

  1. [XSLT] Faire une boucle sur une variable [i]
    Par PoT_de_NuTeLLa dans le forum XSL/XSLT/XPATH
    Réponses: 8
    Dernier message: 07/06/2010, 12h45
  2. Réponses: 1
    Dernier message: 08/12/2009, 08h55
  3. [directsound] boucle de traitement de son
    Par gargle dans le forum DirectX
    Réponses: 5
    Dernier message: 24/03/2003, 10h47
  4. Sortir d'un progamme qui boucle ou qui refresh
    Par mikevador02 dans le forum C
    Réponses: 12
    Dernier message: 14/12/2002, 09h38
  5. Réponses: 2
    Dernier message: 29/05/2002, 20h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo