Quelle est l'utilisation prévue de la else
clause facultative de la try
déclaration?
Quelle est l'utilisation prévue de la else
clause facultative de la try
déclaration?
Réponses:
Les instructions du else
bloc 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 except
capture les erreurs du deuxième appel. Et si vous le mettez après tout le try
bloc, il sera toujours exécuté, et pas avant le finally
. Le else
vous permet de vous assurer
finally
bloc, etIOError
qu'il soulève ne sont pas pris icireturn
, continue
ou break
.
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 except
ne 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".
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.)
Try-else en Python
Quelle est l'utilisation prévue de la
else
clause 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
, continue
et break
peut interrompre le flux de commande else
.
La else
déclaration va s'il n'y a pas d' exceptions et sinon interrompu par un return
, continue
ou break
déclaration.
La
else
clause facultative est exécutée si et quand le contrôle s'écoule à la fin de latry
clause. *
(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
,continue
oubreak
dé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.
else
par rapport au code suivant le try
blocSi vous gérez une erreur, le else
bloc 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!
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à.
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()
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 something
défini la variable si aucune erreur n'est levée. Vous pouvez supprimer cela en dehors du try
bloc, mais cela nécessite une détection compliquée si une variable est définie.
something = a[2]; print something
intérieur du bloc try:?
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.
Des erreurs et des exceptions # Gestion des exceptions - docs.python.org
L'
try ... except
instruction comporte uneelse
clause 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.
En regardant la référence Python, il semble qu'il else
soit exécuté après try
qu'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 try
bloc, 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 catch
bloc, cela pourrait déclencher une autre exception - je suppose que c'est là que le else
bloc est pratique.
try
bloc.
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.
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.
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.
Un else
bloc peut souvent exister pour compléter la fonctionnalité qui se produit dans chaque except
bloc.
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 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 try
blocage de toute façon.
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`
continue
place - le modèle «sortir tôt». Cela vous permet de supprimer la clause "else" et son retrait, ce qui facilite la lecture du code.
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.
break
intérieur try
à la fin, ce qui est plus propre à l'OMI, et vous n'en avez pas besoin else
. De plus, ce continue
n'est pas vraiment nécessaire, vous pouvez simplement pass
.
J'ai trouvé else
utile 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 lock
configuration désactive la surveillance des verrous et ValueErrors enregistre un message d'avertissement utile.
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)
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()
Le else:
bloc est déroutant et (presque) inutile. Cela fait également partie des déclarations for
et while
.
En fait, même sur une if
dé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 if
déclaration, et envisagez même de documenter la else
condition - pour la rendre explicite.
if x > 0: return "yes"
et if x <= 0: return "no"
. Maintenant, une personne vient et change l'une des conditions pour dire x > 1
mais oublie de changer l'autre. Comment cela réduit-il le nombre de bogues qui seraient commis? if else
les 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).