Una excepción es una condición inesperada o inusual, que surge durante la ejecución del programa.
Como su nombre lo indica, se producen cuando la ejecución de una parte del programa no termina correctamente sino que termina de manera excepcional como consecuencia de una situación inesperada. Se suponen que las excepciones ocurren con poca frecuencia.
A estas alturas nos hemos encontrado con numerosas excepciones que tal vez simplemente ignorábamos o se lo atribuíamos a un error en nuestro código, pero no siempre es así. Nuestro código puede ser correcto y aún así, el programa puede dar alguna excepción.
A continuación veremos excepciones típicas para ir entendiendo el concepto.
Una excepción típica es intentar abrir en modo lectura un archivo que no existe. En tal caso la ejecución del programa dará un error y el programa terminará.
archivo = open("d.txt", 'r')
archivo.write("Agregamos una línea")
archivo.close()
Si el archivo datos.txt no existe, el error será el siguiente:
FileNotFoundError: [Errno 2] No such file or directory: d.txt
Prestar atención que el error es FileNotFoundError. Mas adelante veremos como tratar ese error.
Supongamos que nos piden escribir una función divide(a,b) que tiene que retornar el valor de a dividido por b.
def divide(a,b):
return a/b
Puede parecer que no hay problema, pero ¿Qué pasaría si b=0?
ZeroDivisionError: division by zero
En los números naturales, enteros y reales, la división por cero no posee un valor definido por lo tanto la operación da error.
Otra excepción que se suele dar es cuando queremos hacer una conversión de tipos y los datos de entrada no son válidos para la conversión.
dato = int(input("Ingrese un número"))
print("Su número es",dato)
¿Qué pasaría si el usuario ingresa, por ejemplo, una letra?
ValueError: invalid literal for int() with base 10:
Esta excepción se lanza porque no es posible convertir a int una letra.
El manejo de excepciones (Exception handling) permite al programador crear aplicaciones tolerantes a fallos y robustas (resistente a errores). De manera que el programa se ejecuta y no termina de forma inesperada ante la aparición de excepciones.
Es importante aclarar que no todos los lenguajes soportan manejo de excepciones.
Para que un lenguaje soporte excepciones debe proveer:
En casos donde el lenguaje no soporta el manejo de excepciones debe buscarse alguna forma definiendo procedimientos manualmente y agregando sentencias if donde podría ocurrir alguna excepción.
En Python se manejan a través de los bloques try except
while True:
try:
x = int(input("Por favor ingrese un número: "))
break # Para "romper" el while
except ValueError:
print("Oops! No era válido. Intente nuevamente...")
Puede probar el código aquí ingresando distintos valores en el input. (Por defecto, toma los valores "Hola" y 99).
La declaración try funciona de la siguiente manera:
try (el código que está entre try y except)except se saltea y termina la ejecución de la declaración try.try, el resto del bloque se saltea. Luego, si su tipo (en este caso el tipo es ValueError)coincide con la excepción nombrada luego de la palabra except, se ejecuta el bloque except y la ejecución continúa luego de la declaración try except, la excepción se pasa a las declaraciones try de "más afuera". Si no se encuentra nada que la maneje queda como una excepción no manejada y la ejecución termina mostrando la excepción.Es posible declarar varios bloques except para manejar varios tipos de excepciones.
¿Qué sucede cuando una excepción no encuentra un manejador en su bloque try except?
try está contenido dentro de otro y si ese otro tiene un manejador para la excepción. Sino...raiseEl bloque try except se puede complementar opcionalmente con else y finally
else: Se ejecuta en caso que NO se haya producido una excepción.finally: Se ejecuta siempre. Haya ocurrido una excepción o no.
try:
x = int(input("Por favor ingrese un número: "))
except ValueError as e:
print(f"Oops! No era válido.\nCódigo de error: {e}")
except:
print(f"Ha ocurrido un error inesperado")
else:
print("El proceso terminó sin error")
finally:
print("Fin del proceso")
Observar la línea 3. El mensaje de la excepción se guardará en la variable e y la podremos mostrar al usuario. Esto no siempre es así ya que a veces (sobre todo en ambientes productivos) es deseable ocultar los errores de la aplicación y solo mostrar un mensaje genérico o con un código de error.
En Java se usan los bloques try y catch. Pero el funcionamiento es similar.
public class ExcDemo {
public static void main(String[] args) {
int nums[]=new int[4];
try {
System.out.println("Antes de la excepción.");
//generar una excepción de índice fuera de límites
nums[7]=10;
}catch (ArrayIndexOutOfBoundsException exc){
//Capturando la excepción
System.out.println("Índice fuera de los límites!");
} finally {
System.out.println("Después de la excepción.");
}
}
}
Algunas built-in exceptions
ModuleNotFoundError: Se lanza cuando se intenta importar un módulo que no es encontrado.
KeyError: Se lanza cuando se intenta acceder a la key inexistente de un diccionario.
ZeroDivisionError: Se lanza cuando se intenta dividir por cero.
FileNotFoundError: Se lanza cuando se intenta abrir un archivo que no es encontrado en el path indicado.
SyntaxError: Se lanza cuando hay un error de sintaxis.
IndentationError: Es un tipo de SyntaxError, que se lanza específicamente cuando se encuentra un error de identación en el código. Ver herencia de excepciones
TypeError: Se lanza cuando se aplica una función u operación no encontrada para el tipo.
Hay un listado de built-in exceptions que lo pueden consultar en la documentación
El listado de excepciones se centraron en la versión de 3.10 de Python. Para los demás lenguajes pueden ser similares los nombres.
Habiendo visto excepciones es deseable cada vez que nuestro programa pueda lanzar una excepción, el mismo tenga la capacidad de manejarla.
file= "archivo.txt"
try:
with open(file, 'r') as archivo:
for linea in archivo:
print (linea, end='')
except FileNotFoundError:
print("No se ha encontrado el archivo")
En este ejemplo se descartan datos de entrada inválidos y se maneja la excepción por si b=0.
try:
a = int(input("Ingrese un valor para a:"))
b = int(input("Ingrese un valor para b:"))
c = a / b
print(f"a / b = {c}")
except(ValueError, ZeroDivisionError):
print("Ha ocurrido un error")
En caso que a o b den error al convertirse a número o b=0, el mensaje será el mismo.
Es posible levantar o lanzar manualmente una excepción. En Python, se usa la instrucción raise. En Java, se lanza una excepción con las instrucciones throws o throw.
def division_entera(dividendo, divisor):
if divisor == 0:
raise ValueError("El divisor no puede ser cero.")
return dividendo // divisor
# Ejemplo de uso
try:
resultado = division_entera(10, 0)
print("El resultado de la división es:", resultado)
except ValueError as error:
print("Se produjo un error:", error)
¿Para qué puede ser útil lanzar excepciones? ¿Si justamente lo que queremos es evitarlas?. Porque podríamos personalizar los mensajes de error de la excepción de manera de ocultarla al usuario final. También se puede mantener una lista de errores del programa.
Escriba una función que pida al usuario dos valores numéricos. La función debe detectar que realmente se puedan convertir a números los valores ingresados. Usar excepciones para poder validar los datos.
La función debe retornar una tupla con los valores convertidos a números.
Escriba una función que reciba los dos parámetros validados por el ejercicio anterior de los cuales deberá restarlos entre si. La función debe retornar la resta (a-b). En caso que la resta sea negativa, debe lanzar una excepción del tipo ValueError. El programa principal debe manejar la excepción y si llega un ValueError deberá informar que hubo algún error con los valores.