¿Cuál es la razón para que exista try-except-else?
Un try
bloque le permite manejar un error esperado. El except
bloque solo debe capturar excepciones que esté preparado para manejar. Si maneja un error inesperado, su código puede hacer lo incorrecto y ocultar errores.
Se else
ejecutará una cláusula si no hubo errores, y al no ejecutar ese código en el try
bloque, evitará detectar un error inesperado. Nuevamente, detectar un error inesperado puede ocultar errores.
Ejemplo
Por ejemplo:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
return something
La suite "try, except" tiene dos cláusulas opcionales, else
y finally
. Entonces es en realidad try-except-else-finally
.
else
evaluará solo si no hay excepción del try
bloque. Nos permite simplificar el código más complicado a continuación:
no_error = None
try:
try_this(whatever)
no_error = True
except SomeException as the_exception:
handle(the_exception)
if no_error:
return something
así que si comparamos una else
con la alternativa (que podría crear errores), vemos que reduce las líneas de código y podemos tener una base de código más legible, mantenible y con menos errores.
finally
finally
se ejecutará sin importar qué, incluso si otra línea se está evaluando con una declaración de devolución.
Desglosado con pseudocódigo
Podría ser útil desglosar esto, en la forma más pequeña posible que muestre todas las características, con comentarios. Suponga que este pseudocódigo está sintácticamente correcto (pero no se puede ejecutar a menos que se definan los nombres) en una función.
Por ejemplo:
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
Es cierto que podríamos incluir el código en el else
bloque en el try
bloque en su lugar, donde se ejecutaría si no hubiera excepciones, pero ¿y si ese código genera una excepción del tipo que estamos atrapando? Dejarlo en el try
bloque escondería ese error.
Queremos minimizar las líneas de código en el try
bloque para evitar detectar excepciones que no esperábamos, bajo el principio de que si nuestro código falla, queremos que falle en voz alta. Esta es una mejor práctica .
Entiendo que las excepciones no son errores
En Python, la mayoría de las excepciones son errores.
Podemos ver la jerarquía de excepciones usando pydoc. Por ejemplo, en Python 2:
$ python -m pydoc exceptions
o Python 3:
$ python -m pydoc builtins
Nos dará la jerarquía. Podemos ver que la mayoría de los tipos Exception
son errores, aunque Python usa algunos de ellos para cosas como finalizar for
bucles ( StopIteration
). Esta es la jerarquía 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 comentarista preguntó:
Digamos que tiene un método que hace ping a una API externa y desea manejar la excepción en una clase fuera del contenedor API, ¿simplemente devuelve e del método bajo la cláusula except donde e es el objeto de excepción?
No, no devuelve la excepción, simplemente vuelva a subirla con un desnudo raise
para preservar el stacktrace.
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise
O, en Python 3, puede generar una nueva excepción y preservar la traza inversa con el encadenamiento de excepciones:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise DifferentException from the_exception
Elaboro mi respuesta aquí .