Try-else en Python


582

Quelle est l'utilisation prévue de la elseclause facultative de la trydéclaration?


1
La plupart des réponses semblent se concentrer sur les raisons pour lesquelles nous ne pouvons pas simplement mettre le matériel dans la clause else dans la clause try elle-même. La question stackoverflow.com/questions/3996329 demande spécifiquement pourquoi le code de la clause else ne peut pas aller après le bloc try lui-même, et cette question est doublée à celle-ci, mais je ne vois pas de réponse claire à cette question ici. Je pense que stackoverflow.com/a/3996378/1503120 répond parfaitement à cette question. J'ai également essayé d'élucider la signification variée des différentes clauses sur stackoverflow.com/a/22579805/1503120 .
jamadagni

Vous voulez que quelque chose se produise si l'exception ne se déclenche pas, avant le nettoyage final, qui n'est jamais censé déclencher lui-même la même gestion des exceptions.
benjimin

Réponses:


858

Les instructions du elsebloc sont exécutées si l'exécution tombe du bas de la try- s'il n'y a pas eu d'exception. Honnêtement, je n'ai jamais trouvé de besoin.

Cependant, la note relative aux exceptions de gestion :

L'utilisation de la clause else est préférable à l'ajout de code supplémentaire à la clause try car elle évite d'attraper accidentellement une exception qui n'a pas été déclenchée par le code protégé par l'instruction try ... except.

Donc, si vous avez une méthode qui pourrait, par exemple, lancer un IOError, et que vous voulez intercepter les exceptions qu'elle déclenche, mais il y a autre chose que vous voulez faire si la première opération réussit, et vous ne voulez pas intercepter une IOError à partir de cette opération, vous pourriez écrire quelque chose comme ceci:

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
    # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

Si vous venez de mettre another_operation_that_can_throw_ioerror()après operation_that_can_throw_ioerror, la exceptcapture les erreurs du deuxième appel. Et si vous le mettez après tout le trybloc, il sera toujours exécuté, et pas avant le finally. Le elsevous permet de vous assurer

  1. la deuxième opération n'est exécutée que s'il n'y a pas d'exception,
  2. il est exécuté avant le finallybloc, et
  3. tout ce IOErrorqu'il soulève ne sont pas pris ici

7
Gardez également à l'esprit que les variables utilisées dans le bloc try PEUVENT être utilisées dans le bloc else, vous devriez donc toujours envisager d'utiliser cette variante si vous ne vous attendez pas à plus d'exceptions dans le bloc
else

3
Cela n'a pas d'importance, car les variables de portée d'essai sont vues en dehors de l'essai, qu'il y ait autre chose ou non.
Reinderien

36
Il n'y a rien de tel qu'une "variable de portée d'essai". En Python, les portées variables ne sont établies que par des modules, des fonctions et des compréhensions, et non par des structures de contrôle.
mhsmith

9
La clause else vous permet d'écrire du code qui n'a de sens que si une exception n'a pas été levée; la clause except peut simplement passer. Si vous mettez la logique dans le bloc try, vous risquez de cacher silencieusement les bogues dans votre code. N'écrasez jamais les exceptions auxquelles vous ne vous attendiez pas.
Alice Purcell

9
il ne ressort pas clairement de cette réponse ce que signifie «tomber du bas» - non seulement cela se produit à cause d'une exception mais aussi, à cause d'un return, continueou break.
Antti Haapala

108

Il y a une grande raison d'utiliser else- le style et la lisibilité. C'est généralement une bonne idée de conserver le code qui peut provoquer des exceptions près du code qui les traite. Par exemple, comparez-les:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

et

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

Le second est bon lorsque le exceptne peut pas revenir plus tôt ou relancer l'exception. Si possible, j'aurais écrit:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

Remarque: réponse copiée à partir du double récemment publié ici , d'où toutes ces choses "AskPassword".


53

Une seule utilisation: tester du code qui devrait déclencher une exception.

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(Ce code devrait être résumé dans un test plus générique dans la pratique.)


50

Try-else en Python

Quelle est l'utilisation prévue de la elseclause facultative de l'instruction try?

L'utilisation prévue est d'avoir un contexte pour plus de code à exécuter s'il n'y avait aucune exception où il était censé être géré.

Ce contexte évite de gérer accidentellement des erreurs auxquelles vous ne vous attendiez pas.

Mais il est important de comprendre les conditions précises qui causent la clause d' autre à courir, parce que return, continueet breakpeut interrompre le flux de commande else.

En résumé

La elsedéclaration va s'il n'y a pas d' exceptions et sinon interrompu par un return, continueou breakdéclaration.

Les autres réponses manquent cette dernière partie.

De la documentation:

La elseclause facultative est exécutée si et quand le contrôle s'écoule à la fin de la tryclause. *

(Les caractères gras sont ajoutés.) Et la note de bas de page se lit comme suit:

* À l' heure actuelle, le contrôle « s'écoule la fin » , sauf dans le cas d'une exception ou l'exécution d'un return, continueou breakdéclaration.

Il nécessite au moins une clause except précédente ( voir la grammaire ). Donc ce n'est vraiment pas "try-else", c'est "try-except-else (-finally)", le else(et finally) étant optionnel.

Le didacticiel Python détaille l'utilisation prévue:

L'instruction try ... except a une clause else facultative qui, lorsqu'elle est présente, doit suivre toutes les clauses except. Il est utile pour le code qui doit être exécuté si la clause try ne déclenche pas d'exception. Par exemple:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

L'utilisation de la clause else est préférable à l'ajout de code supplémentaire à la clause try car elle évite d'attraper accidentellement une exception qui n'a pas été déclenchée par le code protégé par l'instruction try ... except.

Exemple de différenciation elsepar rapport au code suivant le trybloc

Si vous gérez une erreur, le elsebloc ne s'exécutera pas. Par exemple:

def handle_error():
    try:
        raise RuntimeError('oops!')
    except RuntimeError as error:
        print('handled a RuntimeError, no big deal.')
    else:
        print('if this prints, we had no error!') # won't print!
    print('And now we have left the try block!')  # will print!

Et maintenant,

>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!

26

Try-except-else est idéal pour combiner le modèle EAFP avec la frappe de canard :

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

Vous pourriez penser que ce code naïf est très bien:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

C'est un excellent moyen de masquer accidentellement de graves bogues dans votre code. J'ai typé le nettoyage là-bas, mais l'attributError qui me ferait savoir est avalé. Pire, que se serait-il passé si je l'avais écrit correctement, mais que la méthode de nettoyage était parfois transmise à un type d'utilisateur qui avait un attribut mal nommé, ce qui lui faisait échouer silencieusement à mi-chemin et laisser un fichier non fermé? Bonne chance pour déboguer celui-là.


19

Je trouve cela très utile lorsque vous avez du nettoyage à faire, cela doit être fait même s'il y a une exception:

try:
    data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
    handle_exception(e)
else:
    do_stuff(data)
finally:
    clean_up()

9

Même si vous ne pouvez pas penser à une utilisation en ce moment, vous pouvez parier qu'il doit y avoir une utilisation. Voici un exemple sans imagination:

Avec else:

a = [1,2,3]
try:
    something = a[2]
except:
    print "out of bounds"
else:
    print something

Sans else:

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

Ici, vous avez somethingdéfini la variable si aucune erreur n'est levée. Vous pouvez supprimer cela en dehors du trybloc, mais cela nécessite une détection compliquée si une variable est définie.


3
Quel est le problème avec l' something = a[2]; print somethingintérieur du bloc try:?
S.Lott

@ S.Lott rien, mais que se passe-t-il si quelqu'un vous envoie une liste et que vous ne voulez pas afficher les données si elles ne sont pas assez longues car elles sont probablement corrompues?
Inconnu

12
S. Lott: 'imprimer quelque chose' pourrait déclencher une exception différente que vous ne voulez pas intercepter.
Darius Bacon

Je ne vois pas la différence. Si j'obtiens une exception hors limites, elle affiche "hors limites". C'est compris. Si j'obtiens une autre exception, elle n'est pas détectée par ce bloc de code. Si je ne reçois aucune exception, le comportement consiste à imprimer la valeur de quelque chose, qui est un [2]. Je ne vois pas ce que fait le reste dans cet exemple.
S.Lott

3
La valeur de «quelque chose», lorsqu'elle est imprimée, peut déclencher l'erreur dans sa méthode __str __ (). Bien que cette valeur ne soit en réalité que de 2 dans cet exemple, vous pourriez tout aussi bien signaler qu'il n'y a pas d'exception hors limites ici non plus.
Darius Bacon

8

Il y a un bel exemple de try-else au PEP 380 . Fondamentalement, cela revient à faire une gestion des exceptions différente dans différentes parties de l'algorithme.

C'est quelque chose comme ça:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

Cela vous permet d'écrire le code de gestion des exceptions plus près de l'endroit où l'exception se produit.


7

Des erreurs et des exceptions # Gestion des exceptions - docs.python.org

L' try ... exceptinstruction comporte une elseclause facultative qui, lorsqu'elle est présente, doit suivre toutes les clauses sauf. Il est utile pour le code qui doit être exécuté si la clause try ne déclenche pas d'exception. Par exemple:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

L'utilisation de la clause else est préférable à l'ajout de code supplémentaire à la clause try car elle évite d'attraper accidentellement une exception qui n'a pas été déclenchée par le code protégé par l'instruction try ... except.


6

En regardant la référence Python, il semble qu'il elsesoit exécuté après tryqu'il n'y ait pas d'exception. La clause else facultative est exécutée si et quand le contrôle découle de la fin de la clause try. 2 Les exceptions de la clause else ne sont pas gérées par les clauses except précédentes.

Plongez dans python a un exemple où, si je comprends bien, en trybloc, ils essaient d'importer un module, lorsque cela échoue, vous obtenez une exception et liez par défaut, mais quand cela fonctionne, vous avez une option pour entrer danselse bloc et lier ce qui est requis (voir lien pour l'exemple et l'explication).

Si vous avez essayé de travailler en catchbloc, cela pourrait déclencher une autre exception - je suppose que c'est là que le elsebloc est pratique.


4
"Les exceptions de la clause else ne sont pas gérées par les clauses except précédentes." Voilà la partie utile. Je vous remercie.
geowa4

«La clause else facultative est exécutée si et quand le contrôle s'écoule à la fin de la clause try» est une autre différence, car vous pouvez sortir du trybloc.
Tomer W

4

C'est ça. Le bloc «else» d'une clause try-except existe pour le code qui s'exécute lorsque (et uniquement lorsque) l'opération tentée réussit. Il peut être utilisé et abusé.

try:
    fp= open("configuration_file", "rb")
except EnvironmentError:
    confdata= '' # it's ok if the file can't be opened
else:
    confdata= fp.read()
    fp.close()

# your code continues here
# working with (possibly empty) confdata

Personnellement, je l'aime et je l'utilise quand c'est approprié. Il regroupe sémantiquement les instructions.


2

Peut-être qu'une utilisation pourrait être:

#debug = []

def debuglog(text, obj=None):
    " Simple little logger. "
    try:
        debug   # does global exist?
    except NameError:
        pass    # if not, don't even bother displaying
    except:
        print('Unknown cause. Debug debuglog().')
    else:
        # debug does exist.
        # Now test if you want to log this debug message
        # from caller "obj"
        try:
            if obj in debug:
                print(text)     # stdout
        except TypeError:
            print('The global "debug" flag should be an iterable.')
        except:
            print('Unknown cause. Debug debuglog().')

def myfunc():
    debuglog('Made it to myfunc()', myfunc)

debug = [myfunc,]
myfunc()

Peut-être que cela vous mènera aussi à une utilisation.


2

J'ai trouvé la try: ... else:construction utile dans la situation où vous exécutez des requêtes de base de données et enregistrez les résultats de ces requêtes dans une base de données distincte du même type / type. Disons que j'ai beaucoup de threads de travail traitant toutes les requêtes de base de données soumises à une file d'attente

#in a long running loop
try:
    query = queue.get()
    conn = connect_to_db(<main db>)
    curs = conn.cursor()
    try:
        curs.execute("<some query on user input that may fail even if sanitized">)
    except DBError:
        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of failed query")
        logcurs.close()
        logconn.close()
    else:

        #we can't put this in main try block because an error connecting
        #to the logging DB would be indistinguishable from an error in 
        #the mainquery 

        #We can't put this after the whole try: except: finally: block
        #because then we don't know if the query was successful or not

        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of successful query")
        logcurs.close()
        logconn.close()
        #do something in response to successful query
except DBError:
    #This DBError is because of a problem with the logging database, but 
    #we can't let that crash the whole thread over what might be a
    #temporary network glitch
finally:
    curs.close()
    conn.close()
    #other cleanup if necessary like telling the queue the task is finished

Bien sûr, si vous pouvez distinguer les exceptions possibles qui peuvent être levées, vous n'êtes pas obligé de l'utiliser, mais si le code réagissant à un morceau de code réussi peut lever la même exception que le morceau réussi, et vous ne pouvez pas simplement laissez la deuxième exception possible disparaître, ou revenez immédiatement en cas de succès (ce qui tuerait le thread dans mon cas), alors cela est pratique.


1

Un elsebloc peut souvent exister pour compléter la fonctionnalité qui se produit dans chaque exceptbloc.

try:
    test_consistency(valuable_data)
except Except1:
    inconsistency_type = 1
except Except2:
    inconsistency_type = 2
except:
    # Something else is wrong
    raise
else:
    inconsistency_type = 0

"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""

Dans ce cas, inconsistency_typeest défini dans chaque bloc sauf, de sorte que le comportement est complété dans le cas sans erreur de else.

Bien sûr, je décris cela comme un modèle qui peut apparaître un jour dans votre propre code. Dans ce cas spécifique, vous venez de mettre inconsistency_typeà 0 avant le tryblocage de toute façon.


1

Voici un autre endroit où j'aime utiliser ce modèle:

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

1
Vous pouvez simplement utiliser à la continueplace - le modèle «sortir tôt». Cela vous permet de supprimer la clause "else" et son retrait, ce qui facilite la lecture du code.
malthe

1

L'un des scénarios d'utilisation auxquels je peux penser est celui des exceptions imprévisibles, qui peuvent être contournées si vous essayez à nouveau. Par exemple, lorsque les opérations dans le bloc try impliquent des nombres aléatoires:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

Mais si l'exception peut être prédite, vous devez toujours choisir la validation au préalable sur une exception. Cependant, tout n'est pas prévisible, donc ce modèle de code a sa place.


1
Vous pouvez le faire en mettant l' breakintérieur tryà la fin, ce qui est plus propre à l'OMI, et vous n'en avez pas besoin else. De plus, ce continuen'est pas vraiment nécessaire, vous pouvez simplement pass.
Dirbaio

1

J'ai trouvé elseutile de traiter un fichier de configuration éventuellement incorrect:

try:
    value, unit = cfg['lock'].split()
except ValueError:
    msg = 'lock monitoring config must consist of two words separated by white space'
    self.log('warn', msg)
else:
     # get on with lock monitoring if config is ok

Une exception lors de la lecture de la lockconfiguration désactive la surveillance des verrous et ValueErrors enregistre un message d'avertissement utile.


1

Supposons que votre logique de programmation dépend si un dictionnaire a une entrée avec une clé donnée. Vous pouvez tester le résultat de l' dict.get(key)utilisation de la if... else...construction, ou vous pouvez faire:

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

-1

J'ajouterais un autre cas d'utilisation qui semble simple lors de la gestion des sessions DB:

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()

-17

Le else:bloc est déroutant et (presque) inutile. Cela fait également partie des déclarations foret while.

En fait, même sur une ifdéclaration, le else:peut être abusé de manière vraiment terrible, créant des bogues très difficiles à trouver.

Considère ceci.

   if a < 10:
       # condition stated explicitly
   elif a > 10 and b < 10:
       # condition confusing but at least explicit
   else:
       # Exactly what is true here?
       # Can be hard to reason out what condition is true

Réfléchissez bien else:. C'est généralement un problème. Évitez-le, sauf dans une ifdéclaration, et envisagez même de documenter la elsecondition - pour la rendre explicite.


6
Je serais en désaccord avec celui-ci. Dans le bloc "if-elif", "else" est utilisé car "default" serait utilisé dans le bloc "case" du langage C. Il est toujours recommandé de gérer le cas "par défaut" même si vous pensez avoir couvert tous les cas dans diverses conditions.
Josip

1
@Josip: utilisé comme "par défaut" peut être déroutant. Le problème est de définir clairement la condition qui est ce "défaut". Une condition par défaut mal définie peut être à l'origine du comportement de buggy. Sinon, cela peut être source de confusion. Il faut y réfléchir très attentivement dans tous les cas, pas seulement essayer, pendant et pendant, mais si aussi.
S.Lott

5
Eh bien, le code ci-dessus est totalement abstrait et ne fait rien de significatif, alors oui - pas étonnant qu'il soit déroutant.
julx

1
@ S.Lott "Cela réduirait le bug" - et mon point est que c'est faux. Je pense que nous avons juste une véritable différence d'opinions. Les mauvais programmeurs trouvent toujours des moyens d'écrire des programmes bogués. Toujours. Les bons programmeurs recherchent toujours les bonnes pratiques et peuvent écrire du bon code dans presque tous les langages. L'élimination des constructions utiles donne juste moins de pouvoir aux bons programmeurs sans aider particulièrement les mauvais car ceux-ci sont capables d'inventer un nombre infini de façons de f ** k choses.
julx

5
Considérez: if x > 0: return "yes"et if x <= 0: return "no". Maintenant, une personne vient et change l'une des conditions pour dire x > 1mais oublie de changer l'autre. Comment cela réduit-il le nombre de bogues qui seraient commis? if elseles clauses sont parfois séparées de plusieurs lignes. DRY est une bonne pratique, beaucoup plus souvent qu'autrement, vraiment. (Pardonnez moi pour ce double commentaire).
julx
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.