try...except...else...finally
Es una de esas construcciones que a veces me hace dudar, por eso de que yo vengo de C, y aún se hace raro.
Por ejemplo, hoy me he encontrado con un trozo de código que, aún funcionando, me ha dejado intrigado un buen rato:
# try...except...else...finally y continue for i in [1, 2, 3]: try: print "try", i except: # no va a pasar pass else: print "else", i continue print "no se ejecuta" finally: print "finally", i i += 1 # este incremento no afecta al bucle (es una lista) print "nunca se ejecuta" print "fin con", i
Evidentemente no era ese el código, pero el comportamiento es aproximadamente el mismo. Sabemos que el bloque de código de finally
se ejecutará siempre se dé o no la excepción (en este caso no se dará nunca), aunque se ejecute el continue
:
try 1
else 1
finally 1
try 2
else 2
finally 2
try 3
else 3
finally 3
fin con 4
Así que se ejecuta el bloque del else
y antes de que empiece una nueva interacción del bucle, se ejecuta el bloque de finally
(notar qué partes no se ejecutan). Es el comportamiento esperado.
Ahora, ¿qué pasa con el siguiente código?
# try...except...else...finally y return def test(i): try: print "try", i except: # no va a pasar pass else: print "else", i return i print "no se ejecuta" finally: print "finally", i i += 1 print "nunca se ejecuta" a = test(3) print "fin con", a
Es fácil de seguir, ¿no?
Pues no tanto ;), cualquiera esperaría encontrarse que la ejecución acaba con fin con 4, pero no es así:
try 3
else 3
finally 3
fin con 3
Vemos ese finally 3, así que el bloque de finally
se ejecuta... ¿o no? porque el incremento de la variable no parece llevarse a cabo :D. ¿Alguien se anima a explicar este comportamiento?
Nota: esto es Python 2.7
, aunque no creo que cambie nada (no he probado con Python 3
).
Actualización: el código real en el que he escrito un try...except...else...finally
incluía un eventlet.timeout.Timeout, usando el bloque finally
para cancelar el timeout, con la complicación de que el código a ejecutar controlado por Timeout
podía generar sus propias excepciones (como un socket.timeout
). Simplemente me ha parecido demasiado complicado para comentarlo por aquí ;).
Hay 3 comentarios
Es un ejemplo para ilustrar el comportamiento, no tiene especial sentido :P
finally se utiliza como mecanismo de limpieza. Según la documentación: finally
When a return, break or continue statement is executed in the try suite of a try…finally statement, the finally clause is also executed ‘on the way out.’
Creo entender que cuando se salta al bloque de finally el intérprete guarda el contexto, como si llamaras a una función, y eso incluye el valor de retorno i.
Creo, porque no estoy seguro. Igual no debería preguntar cosas sin saber la respuesta ;)
Gracias por el comentario!
(me parece que más o menos decimos lo mismo)
Exacto, finally se usa para limpiar recursos, el más típico suele ser el de cerrar archivos o conexiones después de hacer una lectura o escritura. Que en caso de fallo igualmente se deben cerrar. Aunque este bloque finally solo puede acceder al contexto local.
Por limpieza y por llevar un control más estricto de lo que se devuelve podrías dejar solo un return debajo del finally y en el bloque try … else … establecer el valor de la variable local. Aunque no se cual de los dos estilos quedará más claro.
por Pablo, en 2011-08-28 11:28:27 ∞
Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.
por Pablo, en 2011-08-25 21:37:10 ∞