Cuando escribo código, a menudo quiero hacer algo como esto:
try:
foo()
except FooError:
handle_foo()
else:
try:
bar()
except BarError:
handle_bar()
else:
try:
baz()
except BazError:
handle_baz()
else:
qux()
finally:
cleanup()
Obviamente, esto es completamente ilegible. Pero está expresando una idea relativamente simple: ejecutar una serie de funciones (o fragmentos de código cortos), con un controlador de excepción para cada una, y detenerse tan pronto como una función falle. Me imagino que Python podría proporcionar azúcar sintáctico para este código, quizás algo como esto:
# NB: This is *not* valid Python
try:
foo()
except FooError:
handle_foo()
# GOTO finally block
else try:
bar()
except BarError:
handle_bar()
# ditto
else try:
baz()
except BazError:
handle_baz()
# ditto
else:
qux()
finally:
cleanup()
Si no se generan excepciones, esto es equivalente a foo();bar();baz();qux();cleanup()
. Si se generan excepciones, las maneja el manejador de excepciones apropiado (si corresponde) y pasamos a cleanup()
. En particular, si bar()
aumenta un FooError
o BazError
, la excepción no se detectará y se propagará a la persona que llama. Esto es deseable por lo que solo detectamos excepciones que realmente esperamos manejar.
Independientemente de la fealdad sintáctica, ¿es este tipo de código una mala idea en general? Si es así, ¿cómo lo refactorizaría? Me imagino que los gestores de contexto podrían usarse para absorber parte de la complejidad, pero realmente no entiendo cómo funcionaría eso en el caso general.
fuente
handle_*
funciones?Respuestas:
¿Qué
handle_foo
hacer? Hay algunas cosas que normalmente hacemos en los bloques de manejo de excepciones.with
Me parece que está haciendo algo extraño en su manejo de excepciones. Su pregunta aquí es un síntoma simple sobre el uso de excepciones de una manera inusual. No estás cayendo en el patrón típico, y es por eso que esto se ha vuelto incómodo.
Sin una mejor idea de lo que estás haciendo en esas
handle_
funciones, eso es todo lo que puedo decir.fuente
handle_
funciones podrían ser fácilmente fragmentos cortos de código que (digamos) registran el error y devuelven valores de reserva, generan nuevas excepciones o hacen cualquier otra cantidad de cosas. Mientras tanto, elfoo()
,bar()
, etc funciones podrían también ser pequeños fragmentos de código. Por ejemplo, podríamos tenerspam[eggs]
, y necesitamos atraparKeyError
.return
ed oraise
d algo, el problema que menciona no surgiría. El resto de la función se omitirá automáticamente. De hecho, una manera simple de resolver su problema sería regresar en todos los bloques de excepción. El código es incómodo porque no está haciendo una fianza o aumento habitual para indicar que se está rindiendo.foo
debería estar haciendo su propia limpieza?foo
indica que algo salió mal y no tiene conocimiento de cuál debería ser el procedimiento de limpieza correcto.Parece que tiene una secuencia de comandos que pueden generar una excepción que debe manejarse antes de regresar. Intente agrupar su código y el manejo de excepciones en ubicaciones separadas. Creo que esto hace lo que pretendes.
fuente
try:
bloque. En este caso, me preocuparíabar()
generar unFooError
, que se manejaría incorrectamente con este código.Hay un par de formas diferentes, según lo que necesite.
Aquí hay una manera con bucles:
Aquí hay una manera con una salida después del error_handler:
Personalmente, creo que la versión en bucle es más fácil de leer.
fuente
En primer lugar, el uso adecuado de a
with
menudo puede reducir o incluso eliminar gran parte del código de manejo de excepciones, mejorando tanto la facilidad de mantenimiento como la legibilidad.Ahora, puede reducir el anidamiento de muchas maneras; otros carteles ya han proporcionado algunos, así que aquí está mi propia variación:
fuente