Manejo de errores y Excepciones en Python

Publicado por Andrea Navarro el

En este artículo exploraremos las excepciones en Python y su uso para controlar los errores que surgen durante la ejecución de un programa. Veremos los tipos predefinidos de excepciones, excepciones personalizadas y los usos de else y finally.

Excepciones

Las excepciones son errores que suceden durante la ejecución de un programa. A diferencia de los errores de sintaxis, no provienen de un error sintáctico del código sino que sólo ocurren durante la ejecución del mismo. Si estas excepciones no son manejadas por el código resultarán en mensajes de error.

a = 15
b = 0
c = 15/0

La ejecución de este código causará una excepción en particular que es lanzada cuando se intenta dividir un valor por 0. El mensaje resultante incluirá el tipo de excepción y un mensaje de error.

Traceback (most recent call last):
  File "archivo.py", line 3, in <module>
    c = 15/0
        ~~^~
ZeroDivisionError: division by zero

Excepciones comunes en Python

Python incluye una lista de excepciones pre-definidas, todas ellas heredan de la clase BaseException. Estas están definidas para lanzarse durante los errores más comunes de ejecución.

ZeroDivisiofornError

Como vimos en el ejemplo anterior, la excepción ZeroDivisiofornError es lanzada cuando se intenta dividir un número por 0.

TypeError

Otra excepción, TypeError, se lanza cuando se realiza una operación con tipos incompatibles.

a = "Texto"
b = 4
c = a + b

La excepción se lanza ya que se está intentando concatenar una variable tipo string con un valor numérico.

TypeError: can only concatenate str (not "int") to str

ValueError

ValueError es lanzada cuando una operación o función recibe un argumento que tiene el tipo correcto pero un valor incorrecto.

a = "texto"
b = int(a)
ValueError: invalid literal for int() with base 10: 'texto'

En este caso la excepción es lanzada porque se intenta convertir a int una variable que contiene un valor string que no puede ser convertido.

IndexError

Se produce la excepción IndexError al intentar acceder a un índice que no existe en una lista u otra estructura indexada.

a = [1, 2, 3]
b = a[5]
IndexError: list index out of range

En este ejemplo se intenta obtener el elemento correspondiente al índice 5 de una lista cuando esta solo contiene 3 elementos.

KeyError

En el caso de los diccionarios si se quiere acceder a un elemento utilizando una clave inexistente se lanza la excepción KeyError

a = {"nombre": "María", "edad": 30}
b = a["apellido"] 
KeyError: 'apellido'

AttrributeError

De manera similar cuando se trabaja con objetos si se quiere acceder a un atributo o método inexistente se lanza la excepción AttributeError.

a = 42
c = a.b
AttributeError: 'int' object has no attribute 'b'

FileNotFoundError

Cuando se quiere abrir un archivo se lanzará FileNotFoundError si este no existe en la ubicación buscada.

with open("archivo.txt", "r") as f:
    contenido = f.read() 
FileNotFoundError: [Errno 2] No such file or directory: 'archivo.txt'

Lista de excepciones comunes en Python

Veamos ahora una lista con las principales excepciones que podemos encontrarnos en nuestros códigos de Python.

Lista de las excepciones más comunes en Python
ZeroDivisionErrorDivisión por cero
OverflowErrorResultado numérico demasiado grande
TypeErrorOperación entre tipos incompatibles
ValueErrorTipo correcto, pero valor inválido
IndexErrorÍndice fuera de rango
KeyErrorClave inexistente en un diccionario
AttributeErrorAtributo o método inexistente en un objeto
NameErrorVariable no definida
UnboundLocalErrorVariable local usada antes de asignarla
FileNotFoundErrorArchivo no encontrado
IsADirectoryErrorSe intentó abrir un directorio como archivo
PermissionErrorNo se tienen permisos suficientes
IOError (alias de OSError)Error genérico de entrada/salida
ImportErrorFallo al importar un módulo
ModuleNotFoundErrorMódulo no encontrado
RuntimeErrorError genérico durante la ejecución
NotImplementedErrorMétodo no implementado en una subclase
MemoryErrorEl programa quedó sin memoria
SyntaxErrorError de sintaxis del código
IndentationErrorError en la indentación del código

Al lanzarse cualquier tipo de excepción se finaliza la ejecución del script a menos que el error sea manejado.

Manejo de errores

Veamos el siguiente fragmento de código:

a = input("Ingrese un número: ")
b = input("Ingrese otro número: ")
resultado = int(a) / int(b)
print("La division es: "+str(resultado))
print("Fin del cálculo")

Si en este script el segundo número ingresado es un 0, se mostrará la excepción correspondiente y no se ejecutarán las lineas de código posteriores.

Ingrese un número: 5
Ingrese otro número: 0
Traceback (most recent call last):
  File "archivo.py", line 3, in <module>
    resultado = int(a) / int(b)
                ~~~~~~~^~~~~~~~
ZeroDivisionError: division by zero

Try y Except

Para manejar este error y permitir que se ejecute el resto del código se utilizan los bloques try y except. Al colocar la sección del código que puede generar una excepción dentro de try es posible especificar como se comportará el programa capturándola con except.

Veamos un ejemplo:

try:
    a = input("Ingrese un número: ")
    b = input("Ingrese otro número: ")    
    resultado = int(a) / int(b)
    print("La división es:", resultado)
except ZeroDivisionError:
    print("Error: No se puede dividir entre cero.")
print("Fin del cálculo")

En este código se han colocado las líneas correspondientes a la carga de valores, división e impresión del resultado dentro del bloque try. Si ocurre una excepción ZeroDivisionError, es decir si se intenta dividir por 0, se ejecutará la impresión correspondiente dentro de except.

Si intentamos cargar 0 como el segundo número obtenemos la siguiente salida:

Ingrese un número: 5
Ingrese otro número: 0
Error: No se puede dividir entre cero.
Fin del cálculo

En lugar de generar un error, el programa se ejecuta hasta el final mostrando el mensaje de error correspondiente.

Sin embargo, veamos qué ocurre si se cargan uno o los dos valores vacíos:

Ingrese un número: 
Ingrese otro número: 
Traceback (most recent call last):
  File "archivo.py", line 5, in <module>
    resultado = int(a) / int(b)  
                ^^^^^^
ValueError: invalid literal for int() with base 10: ''

Esto se debe a que estamos capturando la excepción específica para ZeroDivisionError, pero no estamos manejando la excepción que acaba de generar el código: ValueError. Para hacerlo agregamos la excepción ValueError.

try:
    a = input("Ingrese un número: ")
    b = input("Ingrese otro número: ")    
    resultado = int(a) / int(b) 
    print("La división es:", resultado)    
except ZeroDivisionError:
    print("Error: No se puede dividir entre cero.")
except ValueError:
    print("Error: Debe ingresar dos números.")    

De esta manera ambas excepciones son manejadas en el caso de ocurrir.

Ingrese un número: 5
Ingrese otro número: 0
Error: No se puede dividir entre cero.
Fin del cálculo

else y finally

Además de los bloques try y except, Python permite el uso de los bloques else y finally cuando se utiliza un bloque try. Estas instrucciones permiten controlar mejor el flujo del programa, permitiendo generar comportamientos comunes a diferentes situaciones del código.

Dentro de else se encontrará el código que solo será ejecutado si ninguna excepción ha sido lanzada durante la ejecución del bloque try. Esto puede incluir mostrar el resultado de una operación, actualizar variables luego de hacer validaciones y guardar valores en archivos o bases de datos.

try:
    a = input("Ingrese un número: ")
    b = input("Ingrese otro número: ")    
    resultado = int(a) / int(b)     
except ZeroDivisionError:
    print("Error: No se puede dividir entre cero.")
except ValueError:
    print("Error: Debe ingresar dos números.")    
else:
	print("La división es:", resultado)    
print("Fin del cálculo")

Dentro de finally se colocará el código que deba ejecutarse siempre, independientemente si se lanzó una excepción o no. Se usa generalmente para liberar recursos como cerrar archivos abiertos durante la ejecución del programa, liberar la conexión de la base de datos, o simplemente mostrar un mensaje final.

try:
    a = input("Ingrese un número: ")
    b = input("Ingrese otro número: ")    
    resultado = int(a) / int(b)     
except ZeroDivisionError:
    print("Error: No se puede dividir entre cero.")
except ValueError:
    print("Error: Debe ingresar dos números.")    
else:
	print("La división es:", resultado)    
finally:
	print("Fin del cálculo")

Excepciones personalizadas

Si ninguna de las excepciones predefinidas en Python contiene el tipo que se quiere manejar, es posible definir una nueva excepción creando una clase que herede de Exception.

Continuando el ejemplo anterior, si se quiere que se genere un error cuando el usuario intente dividir por un número con más de tres dígitos, podríamos definir la excepción:

class divisorDemasiadoGrande(Exception):
	"""Se lanza cuando el divisor tiene más de 3 dígitos"""
	pass

Agregando un constructor a la clase es posible definir algunos elementos útiles como el mensaje de error o almacenar información del error.

class divisorDemasiadoGrande(Exception):
	def __init__(self, divisor):
		self.divisor = divisor
		#Mensaje de error
		super().__init__(f"El divisor {divisor} es demasiado grande, ingrese un valor con menos de 4 dígitos")

Para que esta excepción se lance es necesario generar la condición en el código, y lanzarla utilizando raise seguido por el nombre de la clase de la excepción.

En el siguiente ejemplo se verifica si la variable b tiene una longitud de más de tres dígitos, y si es así, se lanza la excepción divisorDemasiadoGrande pasándole como parámetro el valor de b. Este valor será usado para armar el mensaje de error.

Dentro de las excepciones manejadas agregaremos el nombre de la clase divisorDemasiadoGrande y e imprimiremos el mensaje de error.

try:
    a = input("Ingrese un número: ")
    b = input("Ingrese otro número: ")  
    if len(b) > 3:
        raise divisorDemasiadoGrande(b)  
    resultado = int(a) / int(b)     
except ZeroDivisionError:
    print("Error: No se puede dividir entre cero.")
except ValueError:
    print("Error: Debe ingresar dos números.")  
# Imprimir mensaje si se lanza la excepción
except divisorDemasiadoGrande as e:
    print(e)      
else:
	print("La división es:", resultado)    
finally:
	print("Fin del cálculo")

Al ejecutar el código ingresando un divisor con más de 3 dígitos se lanzará la excepción personalizada.

Ingrese un número: 45
Ingrese otro número: 45634
El divisor 45634 es demasiado grande, ingrese un valor con menos de 4 dígitos
Fin del cálculo

En este artículos hemos hecho una introducción a las excepciones en Python y sus usos para controlar errores de ejecución. Es importante recordar que bloque try debe contener únicamente el código que tiene posibilidades de fallar, mientras que el bloque else debe utilizarse para contener pasos seguros si no se produjo ningún error.

También es recomentable utilizar el bloque finally para liberar recursos, como cerrar archivos o conexiones.

Espero que les sirva!




¿Preguntas? ¿Comentarios?

Si tenés dudas, o querés dejarnos tus comentarios y consultas, sumate al grupo de Telegram de la comunidad JuncoTIC!
¡Te esperamos!

Categorías: Programación

Andrea Navarro

- Ingeniera en Informática - Docente universitaria - Investigadora