¿Es una buena práctica usar try-except-else en Python?
De vez en cuando en Python, veo el bloque:
try:
try_this(whatever)
except SomeException as exception:
#Handle exception
else:
return something
¿Cuál es la razón para que el intento-excepto-else exista?
No me gusta ese tipo de programación, ya que está utilizando excepciones para realizar el control de flujo. Sin embargo, si está incluido en el idioma, debe haber una buena razón para ello, ¿no?
Entiendo que las excepciones no son errores, y que solo deben usarse para condiciones excepcionales (por ejemplo, intento escribir un archivo en disco y no hay más espacio, o tal vez no tengo permiso), y no para el control de flujo.
Normalmente manejo las excepciones como:
something = some_default_value
try:
something = try_this(whatever)
except SomeException as exception:
#Handle exception
finally:
return something
O si realmente no quiero devolver nada si ocurre una excepción, entonces:
try:
something = try_this(whatever)
return something
except SomeException as exception:
#Handle exception
9 answers
"no sé si es por ignorancia, pero no me gusta que tipo de programación, ya que está utilizando excepciones para realizar el control de flujo."
En el mundo de Python, el uso de excepciones para el control de flujo es común y normal.
Incluso los desarrolladores del núcleo de Python usan excepciones para el control de flujo y ese estilo está fuertemente integrado en el lenguaje (es decir, el protocolo iterador usa StopIteration terminación de bucle de señal).
Además, el estilo try-except - se usa para evitar las condiciones de carrera inherentes a algunas de las construcciones "look-before-you-leap". Por ejemplo, pruebas os.camino.existe resulta en información que puede estar desactualizada en el momento en que la usa. Del mismo modo, Cola.completo devuelve información que puede estar obsoleta. El estilo try-except-else producirá código más fiable en estos casos.
" Entiendo que las excepciones no son errores, solo deben utilizarse en condiciones excepcionales "
En algunos otros idiomas, esa regla refleja sus normas culturales como se refleja en sus bibliotecas. La "regla" también se basa en parte en consideraciones de rendimiento para esos idiomas.
La norma cultural Python es algo diferente. En muchos casos, debe usar excepciones para control-flow. Además, el uso de excepciones en Python no ralentiza el código circundante y el código de llamada como lo hace en algunos los lenguajes compilados (es decir, CPython ya implementa código para la comprobación de excepciones en cada paso, independientemente de si realmente utiliza excepciones o no).
En otras palabras, su comprensión de que "las excepciones son para lo excepcional" es una regla que tiene sentido en algunos otros lenguajes, pero no para Python.
" Sin embargo, si está incluido en el lenguaje mismo, debe haber un buena razón para ello, ¿no?"
Además de ayudar a evitar las condiciones de carrera, las excepciones también son muy útiles para tirar de los bucles externos de manejo de errores. Esta es una optimización necesaria en lenguajes interpretados que no tienden a tener un movimiento de código invariante de bucle automático .
Además, las excepciones pueden simplificar bastante el código en situaciones comunes donde la capacidad de manejar un problema está muy lejos de donde surgió el problema. Por ejemplo, es común tener código de interfaz de usuario de nivel superior que llama al código para la lógica de negocios que a su vez llama a rutinas de bajo nivel. Las situaciones que surgen en las rutinas de bajo nivel (como los registros duplicados para claves únicas en los accesos a la base de datos) solo se pueden manejar en código de nivel superior (como pedirle al usuario una nueva clave que no entre en conflicto con las claves existentes). El uso de excepciones para este tipo de control-flujo permite que las rutinas de nivel medio ignoren completamente el problema y estén muy bien desacopladas de ese aspecto del control de flujo.
Hay un buen post en el blog sobre la indispensibilidad de excepciones aquí .
También, vea esta respuesta de desbordamiento de pila: ¿Son excepciones realmente para errores excepcionales?
"¿Cuál es la razón para que el intento-excepto-else exista?"
La cláusula else en sí es interesante. Se ejecuta cuando no hay excepción, pero antes de la cláusula finally. Ese es su propósito principal.
Sin la cláusula else, la única opción para ejecutar código adicional antes de la finalización sería la práctica torpe de agregar el código por la cláusula try. Eso es torpe porque arriesga subir excepciones en código que no estaba destinado a ser protegido por el try-block.
El caso de uso de ejecutar código no protegido adicional antes de la finalización no se presenta muy a menudo. Por lo tanto, no esperes ver muchos ejemplos en el código publicado. Es algo raro.
Otro caso de uso para la cláusula else es realizar acciones que deben ocurrir cuando no ocurre ninguna excepción y que no ocurren cuando se manejan excepciones. Para ejemplo:
recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')
Por último, el uso más común de una cláusula else en un bloque de prueba es para un poco de embellecimiento (alinear los resultados excepcionales y los no excepcionales al mismo nivel de sangría). Este uso es siempre opcional y no es estrictamente necesario.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-31 01:10:48
¿Cuál es la razón para que exista el intento-excepto-otra cosa?
Un bloque try
le permite manejar un error esperado. El bloque except
solo debería capturar excepciones que esté preparado para manejar. Si maneja un error inesperado, su código puede hacer lo incorrecto y ocultar errores.
Una cláusula else
se ejecutará si no hubo errores, y al no ejecutar ese código en el bloque try
, evitará detectar un error inesperado. Una vez más, la captura de un error inesperado puede ocultar bichos.
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
. Así que en realidad es try-except-else-finally
.
else
evaluará solo si no hay excepción del bloque try
. 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 un else
con la alternativa (que podría crear errores) vemos que reduce las líneas de código y podemos tener una más legible, mantenible, y menos código base con errores.
finally
finally
se ejecutará sin importar qué, incluso si otra línea está siendo evaluada con una sentencia return.
Desglosado con pseudo-código
Podría ayudar a desglosar esto, en la forma más pequeña posible que muestre todas las características, con comentarios. Supongamos que este pseudocódigo sintácticamente correcto (pero no ejecutable a menos que se definan los nombres) está 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 verdad que podríamos incluir el código en el bloque else
en el bloque try
en su lugar, donde se ejecutaría si no hubiera excepciones, pero ¿qué pasaría si ese código en sí mismo genera una excepción del tipo que estamos capturando? Dejarlo en el bloque try
ocultaría ese error.
Queremos minimizar las líneas de código en el bloque try
para evitar la captura de excepciones que no esperábamos, bajo el principio de que si nuestro código falla, queremos que falle en voz alta. Este es un mejor practice .
Entiendo que las excepciones no son errores{[30]]}
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 de Exception
son errores, aunque Python usa algunos de ellos para cosas como terminar bucles for
(StopIteration
). Esto es Python 3 jerarquía:
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ó:
Supongamos que tiene un método que hace ping a una API externa y desea manejar la excepción en una clase fuera del envoltorio de la API, ¿simplemente devuelve e desde el método bajo la cláusula except donde e es el objeto exception?
No, no se devuelve la excepción, solo se vuelve a subir con un desnudo raise
para preservar la traza apilada.
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 backtrace with exception chaining:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise DifferentException from the_exception
Explico enmi respuesta aquí .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-31 01:14:06
Python no suscribe la idea de que las excepciones solo deben usarse para casos excepcionales, de hecho el modismo es 'pide perdón, no permiso'. Esto significa que el uso de excepciones como parte rutinaria de su control de flujo es perfectamente aceptable y, de hecho, se recomienda.
Esto es generalmente algo bueno, ya que trabajar de esta manera ayuda a evitar algunos problemas (como un ejemplo obvio, las condiciones de carrera a menudo se evitan), y tiende a hacer que el código sea un poco más legible.
Imagine que tiene una situación en la que toma una entrada de usuario que necesita ser procesada, pero tiene un valor predeterminado que ya está procesada. La estructura try: ... except: ... else: ...
hace que el código sea muy legible:
try:
raw_value = int(input())
except ValueError:
value = some_processed_value
else: # no error occured
value = process_value(raw_value)
Compare con cómo podría funcionar en otros idiomas:
raw_value = input()
if valid_number(raw_value):
value = process_value(int(raw_value))
else:
value = some_processed_value
Tenga en cuenta las ventajas. No hay necesidad de comprobar que el valor es válido y analizarlo por separado, se hacen una vez. El código también sigue una progresión más lógica, la ruta del código principal es la primera, seguida de 'si no funciona, haz esto.
El ejemplo es naturalmente un poco artificial, pero muestra que hay casos para esta estructura.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-22 01:59:14
¿Es una buena práctica usar try-except-else en python?
La respuesta a esto es que depende del contexto. Si haces esto:
d = dict()
try:
item = d['item']
except KeyError:
item = 'default'
Demuestra que no conoces muy bien Python. Esta funcionalidad está encapsulada en el método dict.get
:
item = d.get('item', 'default')
El try
/except
block es una forma mucho más visual y detallada de escribir lo que se puede ejecutar eficientemente en una sola línea con un método atómico. Hay otros casos en los que esto es verdad.
Sin embargo, eso no significa que debamos evitar todo manejo de excepciones. En algunos casos se prefiere evitar las condiciones de carrera. No compruebe si existe un archivo, simplemente intente abrirlo y capture el IOError apropiado. En aras de la simplicidad y la legibilidad, trate de encapsular esto o factorizarlo como apropiado.
Lee el Zen de Python, entendiendo que hay principios que están en tensión, y ten cuidado con el dogma que se basa demasiado en cualquiera de las declaraciones.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-03 16:54:25
Debes tener cuidado al usar el bloque finally, ya que no es lo mismo que usar un bloque else en el intento, excepto. El bloque final se ejecutará independientemente del resultado de la excepción de prueba.
In [10]: dict_ = {"a": 1}
In [11]: try:
....: dict_["b"]
....: except KeyError:
....: pass
....: finally:
....: print "something"
....:
something
Como todo el mundo ha señalado, el uso del bloque else hace que su código sea más legible, y solo se ejecuta cuando no se lanza una excepción
In [14]: try:
dict_["b"]
except KeyError:
pass
else:
print "something"
....:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-24 00:53:17
Cada vez que vea esto:
try:
y = 1 / x
except ZeroDivisionError:
pass
else:
return y
O incluso esto:
try:
return 1 / x
except ZeroDivisionError:
return None
Considere esto en su lugar:
import contextlib
with contextlib.suppress(ZeroDivisionError):
return 1 / x
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-13 23:40:49
Este es mi fragmento simple sobre cómo entender try-except-else-finally block en Python:
def div(a, b):
try:
a/b
except ZeroDivisionError:
print("Zero Division Error detected")
else:
print("No Zero Division Error")
finally:
print("Finally the division of %d/%d is done" % (a, b))
Probemos div 1/1:
div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done
Probemos div 1/0
div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-06 06:24:45
Vea el siguiente ejemplo que ilustra todo sobre try-except-else-finally:
for i in range(3):
try:
y = 1 / i
except ZeroDivisionError:
print(f"\ti = {i}")
print("\tError report: ZeroDivisionError")
else:
print(f"\ti = {i}")
print(f"\tNo error report and y equals {y}")
finally:
print("Try block is run.")
Implementarlo y venir por:
i = 0
Error report: ZeroDivisionError
Try block is run.
i = 1
No error report and y equals 1.0
Try block is run.
i = 2
No error report and y equals 0.5
Try block is run.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-25 05:04:02
OP, TIENES RAZÓN. El else después de try/except en Python es feo. conduce a otro objeto de control de flujo donde no se necesita ninguno:
try:
x = blah()
except:
print "failed at blah()"
else:
print "just succeeded with blah"
Un equivalente totalmente claro es:
try:
x = blah()
print "just succeeded with blah"
except:
print "failed at blah()"
Esto es mucho más claro que una cláusula else. El otro después de try / except no se escribe con frecuencia, por lo que toma un momento calcular cuáles son las implicaciones.
Solo porque puedas hacer algo, no significa que debas hacer algo.
Muchas características han sido añadido a los idiomas porque alguien pensó que podría ser útil. El problema es que, cuantas más características, menos claras y obvias son las cosas porque la gente no suele usar esas campanas y silbatos.
Solo mis 5 centavos aquí. Tengo que venir detrás y limpiar una gran cantidad de código escrito por desarrolladores de 1er año fuera de la universidad que piensan que son inteligentes y quieren escribir código de alguna manera uber-apretado, uber-eficiente cuando eso solo hace que sea un desastre para tratar de leer / modificar más tarde. Yo voto por legibilidad todos los días y dos veces los domingos.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-20 16:03:11