Python » Fundamentos de Python » Gestión de Excepciones y Errores en Python

Gestión de Excepciones y Errores en Python

Cuando programamos en Python es normal que cometamos algún error. Dichos errores pueden ser, valga la redundancia, errores de sintaxis o excepciones. El primer caso sucede cuando, por ejemplo, nos olvidamos de indentar un bloque de código dentro de una sentencia if o no introducimos los dos puntos en un bucle for. Un IDE como Visual Studio Code nos puede advertir de dichos errores antes de que ejecutemos nuestro script. Sin embargo, si ejecutamos un script con errores de sintaxis se va a producir un error fatal que va a finalizar dicha ejecución. En este caso el intérprete nos dirá qué es lo que está fallando para que lo solucionemos. Por el contrario, las excepciones son errores que se pueden gestionar para que no se produzca esa terminación incondicional y el script pueda terminar su tarea. En este artículo vamos a profundizar sobre este tema.

Excepciones comunes en Python

La librería estándar de Python incorpora una serie de excepciones que se lanzan cuando se producen ciertos errores. Como existen muchas, aquí sólo vamos a ver algunas de las más comunes. De todos modos, si quieres profundizar sobre esta temática, en la documentación oficial tienes un listado con todas las excepciones de Python.

Antes, comentar que todas estas excepciones heredan de la clase BaseException. Nosotros como programadores también podemos crear nuestras propias excepciones, en cuyo caso tienen que heredar de la clase Exception. Pero este tema es un poco más avanzado y está fuera del propósito de este artículo.

ValueError

El ValueError sucede cuando una operación encuentra un argumento cuyo tipo es correcto pero su valor inapropiado. ¿Qué significa esto? 🤔. Imagina que queremos convertir el string “no soy un número” a un entero con la función int(). Dicha función espera un string, por tanto le estamos pasando un dato de tipo correcto. Sin embargo, el contenido del string no se corresponde con un número, de ahí el error por la imposibilidad de realizar la conversión.

>>> int("esto no es un número")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'esto no es un número'

TypeError

El TypeError sucede cuando se intenta aplicar una operación a un valor cuyo tipo no es el adecuado. Por ejemplo, al intentar sumar un entero con un string.

>>> 1 + "2"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

NameError

El NameError ocurre cuando intentamos acceder a una variable que todavía no se ha definido.

>>> print(no_existe)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'no_existe' is not defined

ZeroDivisionError

Como su nombre indica, el ZeroDivisionError ocurre cuando intentamos dividir un número entre cero.

>>> 1 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

IndexError

El IndexError ocurre cuando, por ejemplo, intentamos acceder a un elemento de una lista proporcionando un índice mayor al del último elemento de la lista. En el siguiente fragmento de código, el índice del último elemento de la lista es 2, por tanto obtenemos un IndexError al intentar acceder al elemento con índice 3.

>>> ["a", "b", "c"][3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

ModuleNotFoundError

El ModuleNotFoundError ocurre cuando intentamos importar un módulo que no tenemos instalado en nuestro entorno de Python.

>>> import moduloinexistente
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'moduloinexistente'

FileNotFoundError

El FileNotFoundError ocurre cuando proporcionamos la ruta de un archivo o fichero que no existe en nuestro sistema de archivos.

>>> open("archivo_inexistente.txt")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'archivo_inexistente.txt'

La sentencia try except

Muchas veces podemos predecir posibles errores que se pueden producir durante la ejecución de nuestro programa. En estos casos, en lugar de dejar que el programa termine, podemos gestionar dichos errores encapsulándolos en una sentencia try except. Veámoslo con un ejemplo:

try:
    edad = int(input("Introduce tu edad (en números): "))
except ValueError:
    print("La edad introducida no es un número")

En este fragmento de código le indicamos al usuario que introduzca su edad en números. Para ello utilizamos la función input(), la cual retorna un string que convertimos a entero con la función int() y guardamos en la variable edad. Todo este código está indentado dentro de un bloque try (con : al final) ya que es susceptible de producir una excepción de tipo ValueError si el usuario introduce letras y/o símbolos. En caso que esto ocurra, se ejecutará el código indentado debajo de la línea except ValueError: y el programa seguirá su curso sin finalizar inesperadamente.

También podemos capturar multiples excepciones separándolas con comas y poniéndolas entre paréntesis:

def dividir(a, b):
    try:
        print(a / b)
    except (TypeError, ZeroDivisionError):
        print("Hubo un error")

En este ejemplo de código, se puede producir una excepción si llamamos a la función dividir() pasándole uno de los dos argumentos como como string, en cuyo caso la división producirá un error de tipo TypeError. Además, también se puede producir una excepción de tipo ZeroDivisionError si b es cero. En ambos casos las excepciones se captura gracias a la línea except (TypeError, ZeroDivisionError): la cual permite que se le muestre al usuario el mensaje “Hubo un error”.

Sin embargo, las excepciones también se pueden gestionar de forma separada, concatenando varios bloques except, para proporcionar al usuario información más detallada sobre lo que ha salido mal, tal y como podemos ver el siguiente fragmento de código:

def dividir(a, b):
    try:
        print(a / b)
    except TypeError:
        print("Los argumentos tienen que ser números")
    except ZeroDivisionError:
        print("No se puede dividir entre 0")

En este caso podemos proporcionar un mensaje personalizado para cada uno de los errores que hemos visto anteriormente.

La cláusula except vacía

En todos los ejemplos vistos anteriormente hemos especificado siempre el tipo de error o errores que pretendemos gestionar. Sin embargo, en Python es perfectamente válido utilizar una cláusula except vacía que capture cualquier tipo de excepción:

try:
    # Hacer algo que pueda dar un error
except:
    print("Ocurrió un error")

Podemos pensar que esto es una buena idea ya que hace nuestro programa robusto evitando que finalice de manera incondicional. Pero esto no es así y se trata de una práctica no recomendada en la guía de estilo PEP 8. Entre otras cosas porque podríamos enmascarar errores en nuestro código. Hemos de tener en cuenta que al capturar las excepciones de forma específica, al producirse un error inesperado, Python nos informa detalladamente sobre lo que ha ocurrido, dándonos información para que podamos arreglar nuestro código.

Foto del autor

Albert Brugués

Soy doctor en informática médica y un apasionado de la tecnología y las nuevas oportunidades que brinda. Más en particular me encanta la inteligencia artificial y el desarrollo web. En este blog pretendo compartir los conocimientos de Python que he ido adquiriendo a lo largo de los años.

Deja un comentario