6. Las excepciones
Ahora nos enfocaremos en las excepciones.
![]() |
El primer script ilustra la necesidad de gestionar las excepciones.
# -*- coding=utf-8 -*-
# se genera un error
x=4/0
Provocamos un error a propósito para ver la información que genera el intérprete. El resultado en pantalla es el siguiente:
La línea 4 nos da:
- el tipo de la excepción: ZeroDivisionError;
- el mensaje de error asociado: división de enteros o módulo por cero. Está en inglés. Es algo que tal vez queramos cambiar.
La regla fundamental en programación es que hay que hacer todo lo posible para evitar fallos «inesperados» como el anterior. Incluso en caso de error, el programa debe terminarse de manera ordenada, proporcionando información sobre el error que se ha producido.
La sintaxis para el manejo de excepciones es la siguiente:
try:
actions susceptibles de lancer une exception
except [classe d'exception, ...]:
actions de gestion de l'exception
finally:
actions toujours exécutées qu'il y ait exception ou non
En el bloque try**, la ejecución de las acciones se detiene tan pronto como surge una excepción. En ese caso, la ejecución continúa con las acciones de la cláusula except**.
La cláusula
except [MyException, ...]:
intercepta las excepciones de tipo MyException o derivadas. Cuando se produce una excepción en un «try», el intérprete revisa las cláusulas «except» asociadas al «try» en el orden en que fueron escritas. Se detiene en la primera cláusula «except» que permita manejar la excepción que se produjo. Si no encuentra ninguna, la excepción se remite al método que la invocó. Si este tiene un bloque try**/except**, la excepción se maneja nuevamente; de lo contrario, continúa remontando la cadena de métodos invocados. En última instancia, llega al intérprete de Python. Este detiene entonces el programa en ejecución y muestra un mensaje de error del tipo mostrado en el ejemplo anterior. Por lo tanto, la regla es que el programa principal debe interceptar todas las excepciones que puedan propagarse desde los métodos llamados.
Una excepción lleva consigo información sobre el error que se ha producido. Esta información se puede obtener con la siguiente sintaxis:
except MyException as informations:
información es una tupla que contiene los datos relacionados con la excepción.
La sintaxis
except MyException, erreur:
asigna a la variable erreur el mensaje de error relacionado con la excepción.
Para lanzar una excepción, se utiliza la sintaxis
raise MyException(param1, param2, ...)
donde, en la mayoría de los casos, MyException es una clase derivada de la clase Exception. Los parámetros pasados al constructor de la clase estarán disponibles en la cláusula except de las estructuras de interceptación de excepciones.
Estos conceptos se ilustran en el siguiente script.
El siguiente script maneja los errores de manera explícita:
# -*- coding=utf-8 -*-
i=0
# se genera un error y se gestiona
x=2
try:
x=4/0
except ZeroDivisionError, erreur:
print ("%s : %s ") % (i, erreur)
# el valor de x no ha cambiado
print "x=%s" % (x)
# volvemos a intentarlo
i+=1
try:
x=4/0
except Exception, erreur:
# se intercepta la excepción más general
print ("%s : %s ") % (i, erreur)
# se pueden interceptar varios tipos de excepciones
i+=1
try:
x=4/0
except ValueError, erreur:
# esta excepción no ocurre aquí
print ("%s : %s ") % (i, erreur)
except Exception, erreur:
# se intercepta la excepción más general
print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# se intercepta un tipo específico
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
# volvemos a intentarlo cambiando el orden
i+=1
try:
x=4/0
except ValueError, erreur:
# esta excepción no ocurre aquí
print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# se intercepta un tipo específico
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
# se intercepta la excepción más general
print ("%s : (Exception) %s ") % (i, erreur)
# una cláusula «except» sin argumentos
i+=1
try:
x=4/0
except:
# no nos interesa la naturaleza de la excepción ni el mensaje de error
print ("%s : il y a eu un probleme ") % (i)
# otro tipo de excepción
i+=1
try:
x=int("x")
except ValueError, erreur:
print ("%s : %s ") % (i, erreur)
# una excepción transporta información en una tupla accesible para el programa
i+=1
try:
x=int("x")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# Se pueden lanzar excepciones
i+=1
try:
raise ValueError("param1","param2","param3")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# Se pueden crear excepciones propias
class MyError(Exception):
pass
# se lanza la excepción MyError
i+=1
try:
raise MyError("info1","info2", "info3")
except MyError as infos:
print ("%s : %s ") % (i, infos)
# se lanza la excepción MyError
i+=1
try:
raise MyError("mon msg d'erreur")
except MyError, erreur:
print ("%s : %s ") % (i, erreur)
# se puede lanzar cualquier tipo de objeto
class Objet:
def __init__(self,msg):
self.msg=msg
def __str__(self):
return self.msg
i+=1
try:
raise Objet("pb...")
except Objet as erreur:
print "%s : %s" % (i, erreur)
# la cláusula finally siempre se ejecuta
# haya o no una excepción
i+=1
x=None
try:
x=1
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
i+=1
x=None
try:
x=2/0
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
Notas:
- líneas 6-9: se maneja una división por cero;
- línea 8: se intercepta la excepción exacta que se produce;
- línea 11: debido a la excepción que se produjo, x no recibió ningún valor y, por lo tanto, no cambió de valor;
- líneas 15-19: se repite el mismo proceso, pero interceptando una excepción de nivel superior del tipo Exception. Como la excepción ZeroDivisionError deriva de la clase Exception, la cláusula except la detendrá;
- líneas 23-33: se incluyen varias cláusulas except para manejar distintos tipos de excepciones. Se ejecutará una sola cláusula except o ninguna si la excepción no cumple con ninguna cláusula «except»;
- líneas 51-55: la cláusula except puede no tener ningún argumento. En ese caso, detiene todas las excepciones;
- líneas 59-62: introducen la excepción ValueError;
- líneas 66-69: se recupera la información transportada por la excepción;
- líneas 73-76: muestran cómo lanzar una excepción;
- líneas 79-84: ilustran el uso de una clase de excepción propia MyError;
- líneas 79-80: la clase MyError simplemente deriva de la clase base Exception. No agrega nada a su clase base. Pero ahora puede nombrarse explícitamente en las cláusulas «except»;
- líneas 97-107: ilustran que en Python, de hecho, se puede lanzar cualquier tipo de objeto y no solo objetos derivados de la clase Exception;
- líneas 109-127: ilustran el uso de la cláusula finally.
Los resultados en pantalla son los siguientes:
0 : integer division or modulo by zero
x=2
1 : integer division or modulo by zero
2 : (Exception) integer division or modulo by zero
3 : (ZeroDivisionError) integer division or modulo by zero
4 : il y a eu un probleme
5 : invalid literal for int() with base 10: 'x'
6 : invalid literal for int() with base 10: 'x'
7 : ('param1', 'param2', 'param3')
8 : ('info1', 'info2', 'info3')
9 : mon msg d'erreur
10 : pb...
11 : finally x=1
12 : exception
12 : finally x=None
Este nuevo script ilustra cómo se transmiten las excepciones a lo largo de la cadena de métodos que las invocan:
# -*- coding=utf-8 -*-
# una excepción propia
class MyError(Exception):
pass
# tres métodos
def f1(x):
# no se manejan las excepciones; estas se propagan automáticamente
return f2(x)
def f2(y):
# no se manejan las excepciones; se propagan automáticamente
return f3(y)
def f3(z):
if (z % 2) == 0:
# si z es par, se lanza una excepción
raise MyError("exception dans f3")
else:
return 2*z
#---------- main
# las excepciones se propagan a lo largo de la cadena de métodos llamados
# hasta que un método las intercepte. En este caso, será main
try:
print f1(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
# un método puede enriquecer las excepciones que reenvía
def f4(x):
try:
return f5(x)
except MyError as infos:
# se enriquece la excepción y luego se vuelve a lanzar
raise MyError(infos,"exception dans f4")
def f5(y):
try:
return f6(y)
except MyError as infos:
# enriquecemos la excepción y luego la lanzamos de nuevo
raise MyError(infos, "exception dans f5")
def f6(z):
if (z % 2) == 0:
# se lanza una excepción
raise MyError("exception dans f6")
else:
return 2*z
#---------- main
try:
print f4(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
Notas:
- líneas 27-30, en la llamada main --> f1 --> f2 --> f3 (línea 28), la excepción MyError lanzada por f3 se propagará hasta main. A continuación, será procesada por la cláusula except de la línea 29;
- líneas 55-58: en la llamada main --> f4 --> f5 --> f6 (línea 56), la excepción MyError lanzada por f6 se propagará hasta main. A continuación, será procesada por la cláusula except de la línea 29. En esta ocasión, a medida que asciende por la cadena de métodos que la invocan, la excepción MyError se enriquece con información agregada por cada método por el que pasa.
Los resultados en pantalla son los siguientes:
