Objetivo: presentar la cláusula finally en el manejo de excepciones en Python.
Imaginemos, por un momento, que soy un perverso programador que, bajo el aspecto de un aparente dócil y simple programa esconde en él una intención oculta. Supongamos que, aprovechándome del deseo de todo el mundo de disponer de una aplicación que permita calcular la división de dos números enteros, intento colar un sutil mensaje subliminal al ejecutarla:
dividendo = int(input('Introduce el dividendo: '))
divisor = int(input('Introduce el divisor: '))
print('El cociente de la división de ambos números es', dividendo//divisor)
print("Viva el Betis manque pierda")
>>>
Introduce el dividendo: 10
Introduce el divisor: 2
El cociente de la división de ambos números es 5
Viva el Betis manque pierda
Impresionante, pero no tanto…
Si por un casual introducimos como divisor un cero, la división no podrá realizarse y el programa abortará, muriendo con él también nuestras oscuras pretensiones:
>>>
Introduce el dividendo: 10
Introduce el divisor: 0
Traceback (most recent call last):
File "C:/Python33/pruebas.py", line 3, in <module>
print('El cociente de la división de ambos números es', dividendo//divisor)
ZeroDivisionError: integer division or modulo by zero
En los artículos anteriores presentamos la captura de excepciones en Python, de modo que tenemos recursos para solucionar esto:
try:
dividendo = int(input('Introduce el dividendo: '))
divisor = int(input('Introduce el divisor: '))
print('El cociente de la división de ambos números es', dividendo//divisor)
except:
print("No ha podido realizarse la operación")
print("Viva el Betis manque pierda")
>>>
Introduce el dividendo: 10
Introduce el divisor: 0
No ha podido realizarse la operación
Viva el Betis manque pierda
Desde luego, esto funciona. La operación de división ha sido protegida con un try/except que, tal como está planteado, intercepta cualquier excepción que pudiera ocurrir. He introducido en el bloque try las dos sentencias input, no sólo la división, pues también ocurriría una excepción si se facilitase un valor no numérico cuando int() intentara la conversión a entero.
El código no fallará ante una eventual división por cero (ZeroDivisionError), la introducción de letras en vez de números (ValueError), o una interrupción con Ctrl-C (KeyboardInterrupt).
Pero no me convence. Quiero emplear mi propio manejador ante una división por cero o la introducción de letras, pero quiero respetar el gestor por omisión que trae Python (el mensaje de error clásico tras abortar el programa) para poder finalizar la ejecución con Ctrl-C si así lo deseo.
Hemos aprendido a interceptar selectivamente las excepciones también:
try:
dividendo = int(input('Introduce el dividendo: '))
divisor = int(input('Introduce el divisor: '))
print('El cociente de la división de ambos números es', dividendo//divisor)
except (ZeroDivisionError, ValueError):
print("No ha podido realizarse la operación")
print("Viva el Betis manque pierda")
Ahora, si dividimos entre cero, salvamos el error y también nuestro mensaje subliminal:
>>>
Introduce el dividendo: 10
Introduce el divisor: 0
No ha podido realizarse la operación
Viva el Betis manque pierda
Lo mismo si introducimos valores no numéricos:
>>>
Introduce el dividendo: GOL!!!
No ha podido realizarse la operación
Viva el Betis manque pierda
Pero nuestro gozo en un pozo si abortamos con Ctrl-C:
>>>
Introduce el dividendo:
Traceback (most recent call last):
File "C:/Python33/pruebas.py", line 2, in <module>
dividendo = int(input('Introduce el dividendo: '))
KeyboardInterrupt
Se rompe la ejecución del programa y, con ello, el Betis pierde.
Para seguir insuflándole ánimos, Python dispone de la claúsula finally en el bloque try. El código que se incluya en ella, apúntate esto bien, se ejecutará siempre, suceda o no suceda una excepción.
En su versión más simple, finally no necesita que haya ningún except:
try:
dividendo = int(input('Introduce el dividendo: '))
divisor = int(input('Introduce el divisor: '))
print('El cociente de la división de ambos números es', dividendo//divisor)
finally:
print("Viva el Betis manque pierda")
Ahora, pase lo que pase dentro del try, la instrucción del finally se ejecutará siempre. Tanto si sucede algo bueno:
>>>
Introduce el dividendo: 10
Introduce el divisor: 2
El cociente de la división de ambos números es 5
Viva el Betis manque pierda
Como si no:
>>>
Introduce el dividendo: 10
Introduce el divisor: 0
Viva el Betis manque pierda
Traceback (most recent call last):
File "C:/Python33/pruebas.py", line 4, in <module>
print('El cociente de la división de ambos números es', dividendo//divisor)
ZeroDivisionError: integer division or modulo by zero
Se ejecuta el print del finally y, a continuación, el manejador por omisión se ocupa del tratamiento de la excepción, abortando el programa e informando por qué.
Podemos combinar finally con except también:
try:
dividendo = int(input('Introduce el dividendo: '))
divisor = int(input('Introduce el divisor: '))
print('El cociente de la división de ambos números es', dividendo//divisor)
except (ZeroDivisionError, ValueError):
print("No ha podido realizarse la operación")
finally:
print("Viva el Betis manque pierda")
En este caso utilizamos nuestro propio gestor ante una división por cero o la introducción de valores no numéricos, pero mantenemos el que trae por defecto Python para cualquier otra excepción, lo que nos permite abortar el programa con Ctrl- C. El código dentro de la claúsula finally se ejecuta siempre, suceda o no suceda una excepción:
>>>
Introduce el dividendo: 10
Introduce el divisor: 4
El cociente de la división de ambos números es 2
Viva el Betis manque pierda
>>> ================================ RESTART ================================
>>>
Introduce el dividendo:
Viva el Betis manque pierda
Traceback (most recent call last):
File "C:/Python33/pruebas.py", line 2, in <module>
dividendo = int(input('Introduce el dividendo: '))
KeyboardInterrupt
El uso típico de la claúsula finally es la realización de tareas de limpieza para que no queden flecos en caso de que se produzca una excepción. Piensa, por ejemplo, en el gesto de cerrar un fichero una vez ha sido abierto para asegurarnos de que, suceda lo que suceda mientras estemos procesando el fichero, siempre quedará perfectamente cerrado.
Dedicaremos el próximo artículo, a modo de resumen, a esquematizar lo que hemos tratado hasta ahora en estos cuatro artículos dedicados a la gestión de excepciones en Python.
Nota: no soy futbolero, ni tengo preferencia por ningún equipo sobre otro, pero sí que admito un cariño y especial admiración por la afición del Betis, tan incondicional, fiel y estoica.
Javier Montero Gabarró
El texto de este artículo se encuentra sometido a una licencia Creative Commons del tipo CC-BY-NC-ND (reconocimiento, no comercial, sin obra derivada, 3.0 unported)
El Club del Autodidacta
Consulta el índice completo de artículos relacionados con Python.