Manejo de excepciones

← Fundamentos de Python ⌂ Home

Objetivos

Desarrollo

Las excepciones son eventos inesperados que ocurren durante la ejecución de un programa. Una excepción puede ser el resultado de un error lógico o una situación no contemplada. Por ejemplo el siguiente código:

x = int(input("Ingresa un número")) #  Posible excepcion, valor no numérico. 
resultado = 10/x # Posible excepción, división entre cero
print(resultado)

'2' + 2 # Posible excepción, error de tipo.

a = [1,2,3]
print(a[5]) # Fuera del rango de la lista. 

Si la excepción no es atrapada causa un error en el código provocando el paro del programa y un reporte con el mensaje apropiado en la consola.

En Python las excepciones son objetos que son lanzados (raised o throw) por el código que encuentra una situación no esperada. Dicha excepción lanzada puede ser atrapada por un bloque (try-except-else-finally) que maneja la excepción de una forma apropiada.

Manejando excepciones

Existen muchas formas de lidiar con las posibles excepciones en el código, por ejemplo si ocurre una división x / y hay un riesgo a que pueda ocurrir la excepción división entre cero. En una situación ideal debemos evitar que nuestro programa nunca alcance un valor de 0 para y. Pero en un caso más complejo y donde la entrada depende de un usuario externo es dificil manejar situaciones del estilo.

Nosotros podríamos evitar que y tenga un valor de 0 con la siguiente condicional:

if y != 0:
    resultado = 10 / y
else:
    … 

Pero el inconveniente de esta solución es que en algún caso más complejo se hace uso de más tiempo de procesamiento tratando de evitar cualquier excepción posible antes de ocurra, si es que ocurre. En Python, lo que se trata de seguir es solamente atrapar el error/excepción cuando ocurra, cuando no, que el programa fluya, y lo hacemos de la siguiente forma:

try:
    y = int(input("Ingresa un numero: "))
    resultado = 10 / y
    print("Si ocurre alguna excepción esta línea nunca será alcanzada")
except ZeroDivisionError:
    print("Division entre cero")
except ValueError:
    print("Ingresa un número entero")

La ventaja de usar el bloque try-except es que en los casos no excepcionales el código se ejecuta de manera eficiente si condicionales innecesarias.

Si quisiéramos obtener un mensaje más claro sobre la excepción ocurrida lo que tenemos que hacer es darle un nombre a nuestra excepció e imprimirlo.

try:
    y = int(input("Ingresa un numero: "))
    resultado = 10 / y
except ZeroDivisionError as z:
    print("Division entre cero: ", z)
except ValueError as t:
    print("Ingresa un número entero: ", t)

La sentencia try-except tiene una cláusula, else, opcional que, cuando está presente, se ejecuta si la cláusula try no genera una excepción. Por ejemplo:

try:
    y = int(input("Ingresa un numero: "))
    resultado = 10 / y
except ZeroDivisionError as z:
    print("Division entre cero: ", z)
except ValueError as t:
    print("Ingresa un número entero ", t)
else: 
    print(resultado)

El uso de la cláusula else es mejor que agregar código adicional a la cláusula try porque evita capturar accidentalmente una excepción que no fue provocada por el código que está siendo protegido por la declaración try-except.

La instrucción try tiene otra cláusula, finally, opcional que pretende definir acciones de limpieza que deben ejecutarse bajo cualquier circunstancia. Por ejemplo:

try:
    y = int(input("Ingresa un numero: "))
    resultado = 10 / y
except ZeroDivisionError as z:
    print("Division entre cero: ", z)
except ValueError as t:
    print("Ingresa un número entero: ", t)
else: 
    print(resultado)
finally: 
    print("Terminó la ejecución de la división")

Una cláusula finally siempre se ejecuta antes de abandonar la instrucción try, ya sea que se haya producido o no una excepción. En aplicaciones del mundo real, la cláusula finally es útil para liberar recursos externos (como archivos o conexiones de red), independientemente de si el uso del recurso fue exitoso.

Excepciones comunes

Algunas de las excepciones más comunes son las siguientes:

Excepciones comunes

Lanzando excepciones

La sentencia raise nos permite forzar una excepción para que ocurra. Por ejemplo, si un código para calcular una raíz cuadrada recibe un valor negativo, podemos generar una excepción de la siguiente manera:

from math import sqrt
x = int(input("Ingresa un número para calcular la raíz")) 
if x < 0:
    raise ValueError("x no puede ser negativa")
print(sqrt(x))

Excepciones propias

Los programas pueden crear sus propias excepciones creando una nueva clase y heredando de exception. Las clases generalmente se mantienen simples. Practicamente solo proveen un nuevo nombre de excepción y nosotros decidimos cuando lanzarlas.

class CadenaLarga(Exception):
    pass

try: 
    x = 'cadena de varios caracteres'
    if len(x) > 10:
        raise CadenaLarga("La cadena ingresada es muy larga")
except CadenaLarga as c: 
    print("Error ", c)

Podemos observar que nosotros decidimos cuando lanzar la excepción, en este caso fue cuando la cadena supera los 10 caracteres de longitud.

Ejercicios

  1. Crear una función own_sqrt(x), que reciba un número entero y realice las validaciones necesarias (x no sea negativa, x sea numérica) para poder calcular la raíz cuadrada, en otro caso que lance una excepción.
  2. Crear un código que pida al usuario ingresar su edad la cual no debe ser negativa, mayor a 120 y no númerica, en esos casos lanzar la excepción correspondiente, atraparla y volver a pedir la entrada del usuario, hasta que sea correcta. La idea es que el código no parece su ejecución hasta que sea correcta la edad.
  3. Cree dos excepciones propias llamadas NoPolinomio e EsImaginario y una función que resuelva la ecuación general de segundo grado validando lo siguiente con excepciones:
    • si a == 0 quiere decir que no es una ecuación de segundo grado, lanzar NoPolinomio
    • si el discriminante es es < que 0 quiere decir que el resultado será imaginario, lanzar EsImaginario

Fuentes