web-dev-qa-db-fra.com

Python try-else

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

506
geowa4

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

Cependant, Gestion des exceptions remarque:

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é généré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 soulève, vous pouvez faire autre chose si la première opération aboutit, et vous ne veut pas attraper une erreur 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 ne mettez que another_operation_that_can_throw_ioerror() après operation_that_can_throw_ioerror, le except interceptera les erreurs du second appel. Et si vous le mettez après tout le bloc try, il sera toujours exécuté, et pas après le finally. La else vous permet de vous assurer que

  1. la deuxième opération ne fonctionne que s'il n'y a pas d'exception,
  2. il est exécuté avant le bloc finally, et
  3. toute IOErrors qu'il soulève ne sont pas attrapés ici
776
Blair Conrad

Il y a une grosse raison d'utiliser le style else - et sa lisibilité. Il est généralement judicieux de conserver un code susceptible de provoquer des exceptions à proximité du code qui les traite. Par exemple, comparez ces:

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

La seconde est bonne lorsque la except ne peut pas revenir plus tôt ou renvoyer 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 doublon récemment posté ici , d'où tout ce "AskPassword".

91
Izkata

Une utilisation: tester du code qui devrait générer 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 en pratique.)

46
Darius Bacon

Python try-else

Quelle est l'utilisation prévue de la clause optionnelle else de l'instruction try?

L'utilisation prévue est d'avoir un contexte pour que plus de code soit exécuté s'il n'y avait aucune exception où il devait être traité.

Ce contexte évite de manipuler accidentellement des erreurs inattendues.

Mais il est important de comprendre les conditions précises qui entraînent l'exécution de la clause else, car return, continue et break peuvent interrompre le flux de contrôle en else.

En résumé

L'instruction else est exécutée s'il existe aucune exception et si elle n'est pas interrompue par une instruction return, continue ou break .

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

à partir de la documentation:

La clause facultative else est exécutée si et quand le contrôle se termine à la fin de la clause try. *

(Bold ajouté.) Et la note de bas de page se lit comme suit:

* Actuellement, le contrôle “coule de la fin” sauf dans le cas d'une exception ou de l'exécution d'une instruction return, continue ou break.

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

Le tutoriel Python explique l'utilisation prévue:

L'instruction try ... except a une clause else optionnelle, qui, lorsqu'elle est présente, doit suivre toutes les clauses except. C'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é générée par le code protégé par l’instruction try ... except.

Exemple différenciant else en fonction du code suivant le bloc try

Si vous gérez une erreur, le bloc else ne sera pas exécuté. 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!
37
Aaron Hall

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

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

Vous pourriez penser que ce code naïf convient parfaitement:

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

C'est un excellent moyen de cacher accidentellement des bugs graves dans votre code. J'ai typo-ed nettoyage là-bas, mais l'attributError qui me permet de savoir est en train d'être avalé. Pire encore, si je l'avais écrit correctement, mais que la méthode de nettoyage recevait parfois un type d'utilisateur doté d'un attribut mal nommé, ce qui entraînait un échec silencieux à mi-parcours et laissait un fichier non fermé? Bonne chance pour déboguer celui-là.

22
Alice Purcell

Je trouve cela très utile lorsque vous avez un 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()
16
RoadieRich

Même si vous ne pouvez pas penser à une utilisation pour le moment, vous pouvez parier que cela doit être utile. 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 la variable something définie si aucune erreur n'est renvoyée. Vous pouvez le supprimer en dehors du bloc try, mais cela nécessite une détection compliquée si une variable est définie.

10
Unknown

De Erreurs et exceptions # Gestion des exceptions - docs.python.org

L'instruction try ... except comporte une clause facultative else, qui, lorsqu'elle est présente, doit respecter toutes les clauses sauf. C'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é générée par le code protégé par l’instruction try ... except.

8
fedorqui

Il y a un bel exemple de try-else dans PEP 38 . Fondamentalement, il s’agit de gérer différentes exceptions 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.

6
itsadok

En regardant référence Python , il semble que else soit exécuté après try quand il n'y a pas d'exception. La clause optionnelle else est exécutée si et quand le contrôle sort de la fin de la clause try. 2 Les exceptions dans la clause else ne sont pas gérées par les clauses d'exception précédentes.

Plongez dans python a un exemple où, si je comprends bien, dans le bloc try, ils essaient d'importer un module. Lorsque cela échoue, vous obtenez une exception et une liaison par défaut, mais lorsque cela fonctionne, vous avez option d'aller dans else bloquer et lier ce qui est requis (voir le lien pour l'exemple et l'explication).

Si vous essayez de travailler dans le bloc catch, il risque de lever une autre exception - je suppose que c'est là que le bloc else est utile.

5
stefanB

C'est ça. Le bloc 'else' d'une clause try-except existe pour le code qui s'exécute quand (et seulement quand) l'opération essayée aboutit. 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 bien et je l’utilise quand cela convient. Il groupe sémantiquement les déclarations.

3
tzot

La plupart des réponses semblent se concentrer sur les raisons pour lesquelles nous ne pouvons pas simplement mettre le matériau dans la clause else de la clause try elle-même. La question la clause else dans l'instruction try ... à quoi sert-elle correctement demande spécifiquement pourquoi le code de la clause else ne peut pas aller après le bloc d'essai lui-même, et cette question est dupée à celle-ci, mais je ne vois pas de réponse claire à cette question ici. Je me sens https://stackoverflow.com/a/3996378/150312 répond parfaitement à cette question. J'ai également essayé d'élucider la signification des différentes clauses de https://stackoverflow.com/a/22579805/150312 .

3
jamadagni

Peut-être 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
DevPlayer

J'ai trouvé la construction try: ... else: utile dans le cas où vous exécutez des requêtes de base de données et que vous enregistrez les résultats de ces requêtes dans une base de données distincte du même type/saveur. Disons que j'ai beaucoup de threads de travail qui traitent 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 faire la distinction entre les exceptions possibles pouvant être émises, vous n’aurez pas à les utiliser, mais si le code réagit à un élément de code réussi, il risque de générer la même exception que l’expérience réussie, 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 fil de discussion dans mon cas), alors cela vous sera utile.

1
sirlark

J'ai trouvé else utile pour 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 à la lecture de lock config désactive la surveillance de verrouillage et ValueErrors enregistre un message d'avertissement utile.

1
Nick

L'un des scénarios d'utilisation auquel je peux penser est constitué par des exceptions imprévisibles, qui peuvent être contournées si vous essayez à nouveau. Par exemple, lorsque les opérations du 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 par rapport à une exception. Cependant, tout ne peut pas être prédit, ce modèle de code a donc sa place.

1
NeoWang

Voici un autre endroit où j'aime utiliser ce motif:

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

Un bloc else peut souvent exister pour compléter les fonctionnalités présentes dans chaque bloc except.

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_type est défini dans chaque bloc sauf, de sorte que le comportement est complété dans le cas d'absence d'erreur dans else.

Bien sûr, je décris cela comme un motif qui peut apparaître dans votre propre code un jour. Dans ce cas particulier, vous devez tout simplement définir inconsistency_type sur 0 avant le bloc try.

1
Wesley

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

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