Quelle est la raison pour laquelle le try-except-else existe?
Un try
bloc vous permet de gérer une erreur attendue. Le except
bloc ne doit intercepter que les exceptions que vous êtes prêt à gérer. Si vous gérez une erreur inattendue, votre code peut faire la mauvaise chose et masquer les bogues.
Une else
clause s'exécutera s'il n'y a pas eu d'erreurs, et en n'exécutant pas ce code dans le try
bloc, vous éviterez d'attraper une erreur inattendue. Encore une fois, la capture d'une erreur inattendue peut masquer les bogues.
Exemple
Par exemple:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
return something
La suite "try, except" a deux clauses facultatives, else
et finally
. C'est donc en fait try-except-else-finally
.
else
n'évaluera que s'il n'y a pas d'exception du try
bloc. Cela nous permet de simplifier le code plus compliqué ci-dessous:
no_error = None
try:
try_this(whatever)
no_error = True
except SomeException as the_exception:
handle(the_exception)
if no_error:
return something
donc si nous comparons un else
à l'alternative (qui pourrait créer des bogues), nous voyons qu'il réduit les lignes de code et nous pouvons avoir une base de code plus lisible, plus facile à entretenir et moins boguée.
finally
finally
s'exécutera quoi qu'il arrive, même si une autre ligne est évaluée avec une instruction de retour.
Décomposé avec un pseudo-code
Cela pourrait aider à décomposer cela, dans la forme la plus petite possible qui montre toutes les fonctionnalités, avec des commentaires. Supposons que ce pseudo-code syntaxiquement correct (mais non exécutable sauf si les noms sont définis) se trouve dans une fonction.
Par exemple:
try:
try_this(whatever)
except SomeException as the_exception:
handle_SomeException(the_exception)
# Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
generic_handle(the_exception)
# Handle any other exception that inherits from Exception
# - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
# Avoid bare `except:`
else: # there was no exception whatsoever
return something()
# if no exception, the "something()" gets evaluated,
# but the return will not be executed due to the return in the
# finally block below.
finally:
# this block will execute no matter what, even if no exception,
# after "something" is eval'd but before that value is returned
# but even if there is an exception.
# a return here will hijack the return functionality. e.g.:
return True # hijacks the return in the else clause above
Il est vrai que nous pourrions inclure le code dans le else
bloc dans le try
bloc à la place, où il s'exécuterait s'il n'y avait pas d'exceptions, mais que faire si ce code lui-même déclenche une exception du type que nous interceptons? Le laisser dans le try
bloc cacherait ce bug.
Nous voulons minimiser les lignes de code dans le try
bloc pour éviter d'attraper des exceptions auxquelles nous ne nous attendions pas, en vertu du principe que si notre code échoue, nous voulons qu'il échoue bruyamment. Il s'agit d'une meilleure pratique .
Je crois comprendre que les exceptions ne sont pas des erreurs
En Python, la plupart des exceptions sont des erreurs.
Nous pouvons afficher la hiérarchie des exceptions en utilisant pydoc. Par exemple, en Python 2:
$ python -m pydoc exceptions
ou Python 3:
$ python -m pydoc builtins
Nous donnera la hiérarchie. Nous pouvons voir que la plupart des types d' Exception
erreurs sont des erreurs, bien que Python en utilise certaines pour des choses comme la fin des for
boucles ( StopIteration
). Voici la hiérarchie de Python 3:
BaseException
Exception
ArithmeticError
FloatingPointError
OverflowError
ZeroDivisionError
AssertionError
AttributeError
BufferError
EOFError
ImportError
ModuleNotFoundError
LookupError
IndexError
KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError
ConnectionAbortedError
ConnectionRefusedError
ConnectionResetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
ProcessLookupError
TimeoutError
ReferenceError
RuntimeError
NotImplementedError
RecursionError
StopAsyncIteration
StopIteration
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError
UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning
DeprecationWarning
FutureWarning
ImportWarning
PendingDeprecationWarning
ResourceWarning
RuntimeWarning
SyntaxWarning
UnicodeWarning
UserWarning
GeneratorExit
KeyboardInterrupt
SystemExit
Un intervenant a demandé:
Supposons que vous ayez une méthode qui envoie une requête ping à une API externe et que vous souhaitiez gérer l'exception dans une classe en dehors de l'encapsuleur d'API, renvoyez-vous simplement e de la méthode sous la clause except où e est l'objet d'exception?
Non, vous ne renvoyez pas l'exception, relancez-la simplement avec un nu raise
pour conserver la trace de pile.
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise
Ou, dans Python 3, vous pouvez déclencher une nouvelle exception et conserver la trace avec chaînage d'exceptions:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise DifferentException from the_exception
J'élabore dans ma réponse ici .