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 :

dico: get avec valeur par defaut ou gestion d'exception ?


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    99
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 99
    Par défaut dico: get avec valeur par defaut ou gestion d'exception ?
    Bonjour tout le monde,

    Je suis en train d'écrire du code ou je fais pas mal appel à des dictionnaire dont les clé ne sont pas forcément présentes et je me demandais ce que vous pensiez des deux types d'écriture possible ?

    A savoir utiliser dict.get() avec une valeure par defaut.
    Code exemple : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    dico = {"key":"val"}
    if dico.get("toto", None) is None:
        'Fais des choses.'

    Ou bien utiliser dict[] avec une gestion d'exception.
    Code exemple : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    dico = {"key":"val"}
    try:
        dico["toto"]
    except KeyError, e
        'Fais des choses.'

    Personnellement pas vraiment un fan de la programmation avec exceptions dans tous les sens, je vais naturellement vers la solution du dict.get. Mais je me demandais ce que vous faisiez ou si vous avez des raisons de préférer une solution plutôt qu'une autre?

    Peut-être y a-t-il une différence de runtime, il faut que je vérifie, tient.

  2. #2
    Membre éprouvé
    Inscrit en
    Avril 2010
    Messages
    99
    Détails du profil
    Informations personnelles :
    Âge : 43

    Informations forums :
    Inscription : Avril 2010
    Messages : 99
    Par défaut
    Bonjour,

    De mon point de vue, on ne devrait utiliser les exceptions seulement en cas "exceptionnel", c'est-à-dire quand le programme ne se déroule pas comme prévu.
    Donc ton cas, je préfère la première solution mais c'est juste mon avis.
    Au niveau performance, il me semble également que la première solution soit plus efficace mais à vérifier.

    Il peut quand même y avoir un problème.
    C'est que la clé "toto" existe et qu'elle soit associée à None (ce qui est différent du fait que la clé "toto" n'existe pas).
    Il faut mieux utiliser
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    dico = {"key":"val"}
    if "toto" not in dico:
        'Fais des choses.'
    pour éviter ce genre de problème.

    Sinon tu peux regarder du coté des defaultdict.
    http://docs.python.org/library/colle...ns.defaultdict

  3. #3
    Membre émérite
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Par défaut
    bonjour,

    histoire d'alimenter le débat, je préfère pour ma part l'approche avec try... except... (dite eafp) plutôt que le if (dite lbyl).

    eafb pour, littéralement: "il est plus facile de demander le pardon que la permission".

    lbyl pour, littéralement: "regarder avant de sauter".

    l'approche try... except... est très efficace, bien plus que la première car il n'y a pas de test à effectuer.

    la performance diminue au fur et à mesure que l'exception se manifeste souvent.

    un peu de lecture ici (un mail d'alex Martelli):

    http://mail.python.org/pipermail/pyt...ay/203039.html

    De mon point de vue, certes très influencé par la lecture de son bouquin (Alex Martelli toujours lui ) on constate que l'approche eafb permet:

    - un code plus compact (bon dans ton cas la condition est simple, elle pourrait ne pas l'être)
    - une meilleure visibilité aussi: on voit directement ce que l'on veut vraiment faire au DEBUT de la structure et les cas particuliers sont traités ensuite alors que dans l'approche lbyl les cas particuliers sont traités à un même niveau.

    Un example que j'aime donner pour illustrer les bienfaits de l'approche eafb est le suivant. Supposons que nous voulions additionner deux entités mais que nous ne savons pas si elles sont compatibles.

    Il serait fastidieux de définir une structure conditionnelle qui permettrait d'identifier dans quel cas de figure on est:

    - 2 entiers
    - 2 réels
    - 1 entier 1 réel
    - 1 entier 1 chaine de caractère
    - etc...

    pour ensuite soit réaliser l'opération à proprement parlé soit lever une exception.

    Alors que l'approche eafb est directe:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    try:
        return a + b
    except TypeError:
        ...

  4. #4
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Salut,

    Le test ci-dessous semble montrer que "try/catch" est moins bon que "key in dict" qui est un peu meilleur que que "get(, default)".
    Mais ce n'est qu'un test...

    Personnellement, j'utilise les 3 formes avec quelques principes:

    - la lisibilité d'abord me fait préférer "key in dict" ou "get default" dans tous les cas où çà peut auto-documenter le code,

    - la lisibilité encore... Aujourd'hui je me suis retrouvé avec des tas de lignes de code qui faisaient plein de key in dict, key in dict,... qui dans le cas False devaient remonter un message d'erreur spécifique.
    J'ai remplacé cela par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @catch_errors()
    def method(...):
         v1 = get_key1()
         v2 = get_key2()
         ...
    def get_key1():
         try:
              v = dict.get(key1)
         except KeyError:
             raise MyError('no key1')
         return v
    - W

    Résultats:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    try_catch            : 0.085 Ms
    in_dict              : 0.083 Ms
    with_default         : 0.082 Ms
    --- and the winner is: 
    with_default         : 1.00%
    in_dict              : 1.01%
    try_catch            : 1.04%
    Code Python : 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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    import random
     
    ENTRIES = 100
    mydict = {}
    for x in range(ENTRIES):
        i = random.randint(1, ENTRIES)
        mydict[i] = i
     
    def try_catch():
        i = random.randint(1, ENTRIES)
        try:
            v = mydict.get(i)
        except KeyError:
            pass
     
    def in_dict():
        i = random.randint(1, ENTRIES)
        if i in mydict:
            v = mydict[i]
     
    def with_default():
        i = random.randint(1, ENTRIES)
        v =  mydict.get (i, None)
     
     
    if __name__ == '__main__':
        from timeit import Timer
        import operator
        COUNT = 20
        REPEAT = 100
        names = ( 'try_catch', 'in_dict', 'with_default' )
        results = list()
        better = float(sys.maxint)
        for name in names:
             t = Timer('%s()' % name, "from __main__ import %s" % name)
             try:
    #            ms = 1000 * t.timeit(number=COUNT) / COUNT
                 ms = 1000 * min(t.repeat(repeat=REPEAT, number=COUNT))/ COUNT
     
             except:
                t.print_exc()
                sys.exit(1)
             print "%-20s : %0.3f Ms" % (name, ms )
             results.append((name, ms))
             if ms < better:
                 better = ms
     
        print '--- and the winner is: '
     
        for item in sorted(results,
                key=operator.itemgetter(1)):
            name, ms = item
            print "%-20s : %0.2f%%" % (name, ms/better )
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre Expert
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Par défaut
    Par ordre de préférence:

    1) collections.defaultdict
    C'est élégant et efficace. C'est moins général que dict.get (la valeur par défaut n'est pas contrôlable au site d'utilisation) ce qui est une bonne chose: plus c'est spécifique, plus c'est facile à comprendre (et donc à s'assurer que c'est correct).

    2) dict.get
    Si defaultdict ne suffit pas, ou si le dict est importé d'un autre module qu'on ne veut/peut pas modifier.

    3) key (not) in dict
    Si dict.get n'est pas assez général (s'il y a un morceau de code substantiel à exécuter si la clé n'est pas dans le dictionnaire, ou s'il est simple mais contient des effets de bords).

    4) try: catch:
    Si le fait que la clé ne soit pas dans le dictionnaire est possible mais est vraiment une situation exceptionnelle (et donc rare). Cela peut être l'inverse également (qu'il soit exceptionnel que la clé soit dans le dictionnaire).

    En fait, la logique là-derrière est que je préfère choisir la forme qui représente le mieux la sémantique que j'ai en tête quand j'écris le code. Les différences de performance ne sont que secondaires, et sont faibles de toute façon.

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    99
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 99
    Par défaut
    Je vois que les avis sont pas mal partagés. En ce qui me concerne, mon approche actuelle est assez proche de celle de Dividee. Je calque mon écriture sur la sémantique de mon code.
    Juste je ne connaissais pas le defaultcict, je vais aller regarder ca de suite. Merci aussi pour la référence MarkMail qui était intéressant a lire.


    En tout cas, merci pour vos réponses.

  7. #7
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Le test ci-dessous semble montrer que "try/catch" est moins bon que "key in dict" qui est un peu meilleur que que "get(, default)".
    Mais ce n'est qu'un test...
    Le test a été effectué avec des clefs entières. C'est très simple à comparer. Ce n'est donc sans doute pas une grosse perte de trouver une clef à deux reprises (if key in dict: dict[key]). Il en va peut-être autrement pour des clefs de type str. Comme vous le dites, « ce n'est qu'un test ».

  8. #8
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Citation Envoyé par Antoine_935 Voir le message
    Le test a été effectué avec des clefs entières. C'est très simple à comparer. Ce n'est donc sans doute pas une grosse perte de trouver une clef à deux reprises (if key in dict: dict[key]). Il en va peut-être autrement pour des clefs de type str.
    Pour moi le but était de comparer les différences dans les méthodes d'accès (1). Quelque soit la méthode d'accès, cela se termine par une fonction de hash et en un parcours de liste pour vérifier si l'élément existe ou pas (2).
    Si on prend des clés compliquées, on augmente à priori (2) dans tous les cas de tests et on 'tasse' les différences.

    => il ne me semblait pas "utile" de compliquer (2) _au contraire_.

    - W

    Pratique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    try_catch            : 0.100 Ms
    in_dict              : 0.098 Ms
    with_default         : 0.097 Ms
    --- and the winner is: 
    with_default         : 1.00%
    in_dict              : 1.01%
    try_catch            : 1.03%
    Le (2) augmente le temps passé, et la variation pour le même test se tasse de 1%... pouièmes mais 'prévisibles'.


    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
    NAME = 'XXXX une tres%(ident)dlongue cle pour voirXXX -%(ident)d'
    get_name = lambda ident: NAME % dict(ident=ident)
     
    for x in range(ENTRIES):
        i = random.randint(1, ENTRIES)
        name = get_name(i)
        mydict[name] = name
     
    def try_catch():
        i = random.randint(1, ENTRIES)
        try:
            v = mydict.get(get_name(i))
        except KeyError:
            pass
     
    def in_dict():
        name = get_name(random.randint(1, ENTRIES))
        if name in mydict:
            v = mydict[name]
     
    def with_default():
        name = get_name(random.randint(1, ENTRIES))
        v =  mydict.get (name, None)
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

Discussions similaires

  1. [AC-2003] requete selection avec valeur par defaut
    Par benoitXV dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 05/03/2010, 15h31
  2. champ type lookup avec valeur par defaut
    Par jeinny dans le forum CRM
    Réponses: 0
    Dernier message: 17/02/2010, 15h44
  3. Réponses: 7
    Dernier message: 29/01/2009, 12h32
  4. Creation de table avec valeur par defaut
    Par Tsukaasa dans le forum JDBC
    Réponses: 0
    Dernier message: 01/09/2008, 17h45
  5. Réponses: 2
    Dernier message: 19/01/2007, 20h00

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