Errores: el pan diario del programador
En el mundo de la programación, los errores son inevitables y forman parte del proceso de aprendizaje y desarrollo. Este documento explora los tipos más comunes de errores que los programadores enfrentan al trabajar con Python, así como algunas estrategias para identificarlos y solucionarlos. A través de ejemplos prácticos, se busca proporcionar una comprensión más profunda de cómo manejar errores y mejorar la calidad del código.

Tipos de Errores en Python
Errores de Sintaxis
Estos errores ocurren cuando se escribe código que no sigue la gramática del lenguaje. Python no puede interpretar o compilar el código debido a un problema de sintaxis.
Ejemplos:
- Olvidar cerrar un paréntesis o una comilla:
print("Hola mundo)
- Uso incorrecto de una palabra clave:
def = 5
Errores de Tiempo de Ejecución
Estos errores ocurren mientras el programa está en ejecución, a pesar de que el código se haya escrito correctamente desde el punto de vista sintáctico.
Tipos comunes de errores de ejecución:
- Error de
IndentationError
: Se produce cuando no se respeta la indentación del código.def saludo(): print("Hola")
- Error de
TypeError
: Se produce cuando se intenta realizar una operación con un tipo de datos incompatible.resultado = "Hola" + 5
- Error de
ValueError
: Sucede cuando se pasa un argumento a una función que no es adecuado, aunque su tipo de datos sea correcto.int("abc")
- Error de
IndexError
: Se da cuando se intenta acceder a un índice fuera del rango de una lista.lista = [1, 2, 3] print(lista[5])
- Error de
KeyError
: Ocurre cuando se intenta acceder a una clave que no existe en un diccionario.diccionario = {"nombre": "Juan"} print(diccionario["edad"])
- Error de
AttributeError
: Se genera cuando se intenta acceder a un atributo o método que no existe en un objeto.objeto = "Hola" objeto.append(" Mundo")
- Error de
ZeroDivisionError
: Ocurre cuando se intenta dividir un número entre cero.x = 5 / 0
- Error de
FileNotFoundError
: Se produce cuando se intenta abrir un archivo que no existe.open("archivo_inexistente.txt")
Errores Lógicos
Los errores lógicos son más difíciles de detectar, ya que el código se ejecuta sin problemas, pero no produce el resultado esperado. Son errores en la lógica del programa.
Ejemplo.
def calcular_area(base, altura):
return base * base # Error lógico, debería ser base * altura
Errores de Excepción
En Python, las excepciones son objetos que representan errores durante la ejecución de un programa. Estos errores pueden ser controlados mediante bloques try-except
. Algunas excepciones comunes incluyen:
Errores de Excepción Comunes
-
BaseException
: Clase base para todas las excepciones en Python. Es la raíz del sistema jerárquico de excepciones. -
Exception
: Clase base para las excepciones estándar, utilizada para capturar errores en programas normales.
Errores Relacionados con Argumentos o Tipos
-
TypeError
: Error generado cuando una operación o función se aplica a un objeto del tipo inapropiado. -
ValueError
: Error lanzado cuando una función recibe un argumento del tipo correcto pero con un valor inapropiado. -
KeyError
: Error lanzado al intentar acceder a una clave inexistente en un diccionario. -
IndexError
: Error que ocurre al intentar acceder a un índice fuera de rango en una lista, tupla, etc. -
AttributeError
: Error generado cuando se intenta acceder a un atributo inexistente de un objeto. -
NameError
: Error que ocurre cuando se utiliza una variable no definida en el espacio de nombres. -
UnboundLocalError
: Subclase de NameError. Aparece cuando una variable local se utiliza antes de ser asignada.
Errores Matemáticos
-
ArithmeticError
: Clase base para errores en operaciones aritméticas. -
ZeroDivisionError
: Error al intentar dividir por cero. -
OverflowError
: Error cuando un cálculo produce un número demasiado grande para ser representado. -
FloatingPointError
: Error relacionado con operaciones en punto flotante.
Errores de Entrada/Salida
-
IOError
: Error general de entrada/salida (obsoleto, ahora se utiliza OSError). -
OSError
: Error relacionado con operaciones del sistema operativo (lectura/escritura de archivos, etc.). -
FileNotFoundError
: Error lanzado cuando no se encuentra un archivo o directorio. -
PermissionError
: Error que ocurre al intentar realizar una operación sin los permisos necesarios. -
IsADirectoryError
: Error al tratar de operar en un directorio como si fuera un archivo. -
NotADirectoryError
: Error al tratar de operar en un archivo como si fuera un directorio. -
EOFError
: Error lanzado cuando una función de entrada llega al final del archivo sin datos adicionales.
Errores de Memoria
-
MemoryError
: Error que ocurre cuando el sistema no tiene memoria suficiente para completar una operación. -
RecursionError
: Error lanzado cuando se supera el límite de recursión.
Errores de Importación
-
ImportError
: Error que ocurre al intentar importar un módulo que no existe. -
ModuleNotFoundError
: Subclase de ImportError. Aparece específicamente cuando no se encuentra un módulo.
Errores de Codificación y Descodificación
-
UnicodeError
: Clase base para errores relacionados con codificación y descodificación de Unicode. -
UnicodeEncodeError
: Error al codificar datos en Unicode. -
UnicodeDecodeError
: Error al descodificar datos en Unicode. -
UnicodeTranslateError
: Error al traducir datos en Unicode.
Errores de Concurrencia
-
BlockingIOError
: Error que ocurre cuando una operación de E/S bloqueante es interrumpida. -
TimeoutError
: Error generado cuando expira el tiempo de espera de una operación.
Errores Personalizables
-
RuntimeError
: Error general que no cae bajo otras categorías específicas. -
NotImplementedError
: Error utilizado para indicar que un método o función no ha sido implementado.
Errores de Clases y Herencia
-
LookupError
: Clase base para errores relacionados con búsquedas de índices o claves. -
AssertionError
: Error que ocurre cuando una declaración assert falla. -
SystemError
: Error que indica un problema interno en el intérprete de Python.
Errores de Sistema y Procesos
-
KeyboardInterrupt
: Error lanzado cuando se presiona Ctrl+C durante la ejecución de un programa. -
SystemExit
: Excepción lanzada para solicitar la finalización de un programa. -
StopIteration
: Error lanzado para señalar el final de un iterador. -
StopAsyncIteration
: Error lanzado para detener iteradores asíncronos.
Errores de Importación
Este tipo de error ocurre cuando Python no puede encontrar el módulo o paquete especificado.
Ejemplo:
import modulo_inexistente
Errores de Recursión
Este error ocurre cuando una función recursiva no tiene una condición de parada adecuada y se llama infinitamente.
Ejemplo:
def funcion_recursiva(): return funcion_recursiva()Estrategias para Manejar Errores
Dinámica de Tratamiento de Excepciones
Cuando el código intenta realizar una operación incorrecta, poco razonable, irresponsable, ilógica o que simplemente no es válida, Python responde de la manera:
- Interrumpe la ejecución del programa de forma inmediata, evitando que el error se propague o cause resultados inesperados.
- Genera un objeto especial denominado excepción, que es un tipo particular de dato diseñado para representar y manejar el error ocurrido.
Las excepciones permiten identificar qué salió mal y, en muchos casos, ofrecen la oportunidad de gestionar el error de manera controlada para evitar que el programa falle por completo.
El proceso detallado de la generación de excepciones en Python, cuando ocurre un error, sigue varios pasos bien definidos. A continuación se desglosa el proceso:
- Detección del error: Cuando el intérprete de Python encuentra una operación inválida (como dividir por cero, acceder a un índice fuera de rango o usar un tipo de dato incorrecto), detecta que la operación no puede continuar de forma normal.
Ejemplo:x = [1, 2, 3] print(x[5]) # Operación inválida (acceso a un índice fuera de rango)
- Creación de una excepción: En el momento del error, Python crea un objeto especial llamado excepción, basado en una clase predefinida. Este objeto contiene información detallada sobre el error, como:
- El tipo de excepción: Por ejemplo,
ZeroDivisionError
,IndexError
,TypeError
, etc. - Un mensaje descriptivo: Explica la naturaleza del problema.
- El contexto del error: Información sobre dónde ocurrió, como el archivo, la línea y la pila de llamadas (stack trace).
- El tipo de excepción: Por ejemplo,
- Lanzamiento (raising) de la excepción: Python "lanza" la excepción para indicar que ocurrió un error. Esto interrumpe inmediatamente la ejecución normal del programa y comienza a buscar un manejador de excepciones adecuado en el código (un bloque try...except). Ejemplo:
x = [1, 2, 3] print(x[5]) # Genera IndexError
list index out of range
. - Propagación de la excepción: Python revisa si la excepción puede ser manejada dentro del bloque de código donde ocurrió el error:
- Si no encuentra un bloque
try...except
adecuado en el contexto actual, la excepción se "propaga" hacia afuera, buscando manejadores en niveles superiores de la pila de llamadas. - Si llega al nivel más alto sin encontrar un manejador, la excepción no es manejada.
- Si no encuentra un bloque
- Terminación del programa o manejo de la excepción:
- Sin manejo de la excepción: Si no hay un bloque
try...except
que gestione la excepción, Python imprime un mensaje de error detallado (traceback), que incluye:- La excepción lanzada.
- El mensaje descriptivo del error.
- La ubicación exacta del error en el código. El programa se termina de forma abrupta.
print(10 / 0) # Sin bloque try...except # Resultado: # ZeroDivisionError: division by zero
- Con manejo de la excepción: Si se encuentra un bloque
try...except
, el código dentro del bloqueexcept
se ejecuta, permitiendo que el programa continúe su ejecución de manera controlada.try: print(10 / 0) except ZeroDivisionError as e: print(f"Ocurrió un error: {e}") # Resultado: # Ocurrió un error: division by zero
- Sin manejo de la excepción: Si no hay un bloque
- Terminación del proceso:
- Si la excepción es manejada correctamente, el programa puede continuar su ejecución normal.
- Si no se maneja, el programa se cierra después de imprimir el traceback.
Uso de Try-Except
El bloque try...excepts
en Python es una estructura utilizada para manejar excepciones (errores) que pueden ocurrir durante la ejecución de un programa. Este mecanismo permite que el programa continúe ejecutándose en lugar de detenerse abruptamente cuando ocurre un error. A continuación, se describe detalladamente su uso y funcionamiento:
Estructura Básica
La estructura básica de un bloque try...except
es la siguiente:
try:
# Código que puede generar una excepción
operación_potencialmente_riesgosa()
except TipoDeExcepción:
# Código que se ejecuta si ocurre una excepción del tipo especificado
manejar_la_excepción()
-
try
: En este bloque se coloca el código que podría generar un error. Python intentará ejecutarlo. -
except
: Aquí se especifica qué hacer si se produce una excepción. Se puede manejar un tipo específico de excepción o cualquier excepción.
Tipos de Manejo de Excepciones
- Capturar una excepción específica: Es posible manejar un tipo de excepción específico, como
ValueError
,ZeroDivisionError
, o cualquier otra.try: x = int(input("Introduce un número: ")) resultado = 10 / x except ValueError: print("Se debe ingresar un número válido.") except ZeroDivisionError: print("No se puede dividir entre cero.")
- Capturar múltiples excepciones: Si varias excepciones deben manejarse de la misma manera, se pueden agrupar usando una tupla.
try: x = int(input("Introduce un número: ")) resultado = 10 / x except (ValueError, ZeroDivisionError): print("Ocurrió un error: número inválido o división entre cero.")
- Capturar cualquier excepción: Utilizando un
except
sin especificar el tipo, se capturan todas las excepciones.try: x = int(input("Introduce un número: ")) resultado = 10 / x except: print("Ocurrió un error inesperado.")
- Obtener detalles de la excepción: La excepción puede ser capturada en una variable para acceder a información detallada.
try: x = int(input("Introduce un número: ")) resultado = 10 / x except Exception as e: print(f"Ocurrió un error: {e}")
- Excepciones generadas en una Función: Si una excepción es generada dentro de una función, puede ser manejada:
Bloques else y finally
-
else
: Este bloque se ejecuta si no se genera ninguna excepción dentro del bloquetry
:try: x = int(input("Introduce un número: ")) resultado = 10 / x except ZeroDivisionError: print("No se puede dividir entre cero.") else: print(f"El resultado es {resultado}")
-
finally
: Este bloque siempre se ejecuta, ya sea que ocurra o no una excepción. Se utiliza para realizar limpieza de recursos, como cerrar archivos o conexiones.try: archivo = open("datos.txt", "r") contenido = archivo.read() except FileNotFoundError: print("El archivo no existe.") finally: print("Cerrando el archivo.") archivo.close()
Importancia del manejo de excepciones
- Evita interrupciones inesperadas: Un programa bien diseñado no debería cerrarse abruptamente debido a errores no manejados.
- Facilita la depuración: Proporciona información específica sobre los errores.
- Mejora la experiencia del usuario: Ofrece mensajes claros y opciones en lugar de mostrar rastros de errores confusos.
Ejemplo Práctico Completo
Supongamos que se desea leer números de un archivo y calcular el recíproco de cada uno:
try:
with open("numeros.txt", "r") as archivo:
for linea in archivo:
numero = int(linea.strip())
print(f"El recíproco de {numero} es {1/numero}")
except FileNotFoundError:
print("El archivo no fue encontrado.")
except ValueError:
print("El archivo contiene datos no válidos.")
except ZeroDivisionError:
print("Uno de los números es cero, no se puede dividir.")
finally:
print("Proceso completado.")
Depuración
La depuración de código en Python es el proceso de identificar y corregir errores (bugs) en un programa. A continuación, se describen las principales técnicas y herramientas que se utilizan para depurar código en Python:
Uso de Declaraciones print()
Agregar declaraciones print()
estratégicamente en el código ayuda a inspeccionar el flujo de ejecución y los valores de las variables.
Ventajas:
- Fácil de usar.
- No requiere herramientas externas.
def suma(a, b):
print(f"Valores recibidos: a={a}, b={b}") # Depuración
return a + b
resultado = suma(5, '3') # Error intencional para identificar
print(f"Resultado: {resultado}")
Uso de un Depurador (Debugger)
Un depurador permite inspeccionar el estado del programa línea por línea, verificar valores de variables y entender mejor el flujo del programa.
- Módulo
pdb
(Python Debugger): Python incluye un depurador integrado llamadopdb
.
Uso básico:
- Insertar
import pdb; pdb.set_trace()
en el punto donde se desea iniciar la depuración. - Ejecutar el programa y usar comandos de pdb.
import pdb def suma(a, b): pdb.set_trace() # Pausa la ejecución para depuración return a + b resultado = suma(5, 3) print(f"Resultado: {resultado}")
-
n
: Ejecuta la siguiente línea. -
c
: Continúa hasta el siguiente punto de interrupción. -
l
: Muestra las líneas de código actuales. -
p <variable>
: Imprime el valor de una variable. -
q
: Salir del depurador.
- Insertar
- Depuración en IDEs: La mayoría de los entornos de desarrollo integrados (IDEs) como PyCharm, Visual Studio Code y Thonny tienen depuradores visuales. Estos permiten:
- Establecer puntos de interrupción (breakpoints).
- Inspeccionar valores de variables en tiempo real.
- Paso a paso por el código (step over, step into, step out).
Registro de Eventos (Logging)
El módulo logging
permite registrar información sobre la ejecución del programa en un archivo o en la consola, lo que facilita la depuración.
import logging
logging.basicConfig(level=logging.DEBUG)
def suma(a, b):
logging.debug(f"Valores recibidos: a={a}, b={b}")
return a + b
resultado = suma(5, 3)
print(f"Resultado: {resultado}")
Análisis Estático del Código
Herramientas como flake8
, pylint
y mypy
ayudan a encontrar errores comunes y verificar la tipificación estática.
Ejemplo con flake8: Ejecutar en la terminal:
flake8 mi_codigo.py
Depuración de Memoria y Rendimiento
Para problemas de memoria o rendimiento, se pueden usar herramientas como:
-
cProfile
: Para analizar el tiempo de ejecución. -
memory_profiler
: Para monitorear el uso de memoria.
Ejemplo con cProfile
:
python -m cProfile mi_codigo.py
Pruebas Unitarias
Escribir pruebas unitarias con bibliotecas como unittest o pytest ayuda a identificar errores potenciales antes de que ocurran en producción.
Ejemplo con unittest:
import unittest
def dividir(a, b):
if b == 0:
raise ValueError("No se puede dividir por cero.")
return a / b
class TestDivision(unittest.TestCase):
def test_division_correcta(self):
self.assertEqual(dividir(10, 2), 5)
def test_division_por_cero(self):
with self.assertRaises(ValueError):
dividir(10, 0)
La Anatomía de las Excepciones
- No se puede agregar más de un bloque
except
sin nombre (anónimo) después de los bloques con nombre.# Este es el código principal que siempre se ejecuta sin problemas try: # Sección del código que puede generar excepciones print("Ejecutando código riesgoso...") # Código riesgoso aquí (por ejemplo, abrir un archivo, realizar operaciones aritméticas, etc.) except Except_1 as e: # Manejo específico para la excepción `Except_1` print(f"Se manejó un error de tipo Except_1: {e}") except Except_2 as e: # Manejo específico para la excepción `Except_2` print(f"Se manejó un error de tipo Except_2: {e}") except Exception as e: # Manejo para cualquier otro tipo de excepción no específica print(f"Se manejó una excepción no esperada: {e}") # Esto siempre se ejecutará, incluso si hubo error.
- Todas las excepciones de Python predefinidas forman una jerarquía, es decir, algunas de ellas son más generales (la llamada
BaseException
es la más general) mientras que otras son más o menos concretas (por ejemplo,IndexError
es más concreta queLookupError
).
No se debe poner excepciones más concretas antes de las más generales dentro de la misma secuencia de bloques except. Por ejemplo, se puede hacer esto:try: # Código riesgoso. except IndexError: # Solucionando problemas con listas. except LookupError: # Lidiando con búsquedas erróneas.
try: # Código riesgoso. except LookupError: # Lidiando con búsquedas erróneas. except IndexError: nunca llegarás aquí
- La sentencia de Python
raise NombreDeExcepción
puede generar una excepción bajo demanda. La misma sentencia pero sinNombreDeExcepción
, se puede usar solamente dentro del bloqueexcept
, y genera la misma excepción que se está manejando actualmente.edad = int(input("Introduce tu edad: ")) if edad < 0: raise ValueError("La edad no puede ser negativa.") elif edad < 18: raise PermissionError("Debes ser mayor de edad para continuar.") else: print("Bienvenido, eres mayor de edad.")
- Si numero es mayor que 0, se imprimirá:
ValueError: La edad no puede ser negativa
. - Si numero es mayor que 15, se imprimirá:
PermissionError: Debes ser mayor de edad para continuar
. - Si numero es mayor que 20, se imprimirá: Bienvenido, eres mayor de edad.
- Si numero es mayor que 0, se imprimirá:
- La sentencia
assert
en Python permite verificar una condición booleana durante el desarrollo y las pruebas. Si la expresión evaluada esFalse
,0
,None
, o una cadena vacía, se genera una excepciónAssertionError
. Esta herramienta es útil para validar suposiciones y detectar errores en puntos críticos del código, ayudando a identificar problemas durante el desarrollo y garantizando la integridad de los datos.
Si se desea estar seguro que un número sea positivo:numero = 5 assert numero > 0, "El número no es positivo." print("El número es positivo.")
- Si
numero
es mayor que 0, se imprimirá:El número es positivo
. - Si
numero
fuera negativo o cero, se lanzaría:AssertionError: El número no es positivo
.
- Si
Excepciones Integradas
Se procederá a presentar una breve lista de las excepciones más útiles. Aunque pueda parecer extraño denominar "útil" a un elemento o fenómeno que representa una señal visible de falla o retroceso.
Las excepciones son tan rutinarias y normales como cualquier otro aspecto de la vida de un programador, constituyendo una herramienta fundamental para manejar situaciones imprevistas en el desarrollo de software.
A continuación, se presenta un listado de las excepciones más importantes en Python, ordenadas de las más generales a las más específicas. Para cada una, se incluyen su nombre, ubicación en el árbol de excepciones, una breve descripción y un ejemplo conciso de código:
- BaseException:
- Ubicación: Raíz del árbol de excepciones.
- Descripción: Clase base para todas las excepciones en Python.
- Ejemplo:
raise BaseException("Esto es una excepción general")
- Exception:
- Ubicación: Ubicación: Hija directa de BaseException.
- Descripción: Descripción: Clase base para todas las excepciones manejables.
- Ejemplo:
raise Exception("Excepción genérica")
- ArithmeticError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Clase base para errores en operaciones aritméticas.
- Ejemplo:
x = 1 / 0 # Genera un ZeroDivisionError, hijo de ArithmeticError
- ZeroDivisionError:
- Ubicación: Ubicación: Hija de ArithmeticError.
- Descripción: Descripción: Ocurre cuando se intenta dividir por cero.
- Ejemplo:
x = 10 / 0
- OverflowError:
- Ubicación: Ubicación: Hija de ArithmeticError.
- Descripción: Descripción: Se genera cuando un resultado excede el rango permitido.
- Ejemplo:
"import math math.exp(1000)"
- AssertionError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Se lanza cuando una aserción assert falla.
- Ejemplo:
assert 2 + 2 == 5, "Aserción fallida"
- AttributeError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Se produce cuando un objeto no tiene un atributo solicitado.
- Ejemplo:
"obj = 5 obj.nonexistent_attribute"
- EOFError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Ocurre cuando la función input() encuentra el final de archivo.
- Ejemplo:
input("Ingresa algo: ") # Genera EOFError si se presiona Ctrl+D (Linux/Mac) o Ctrl+Z (Windows)
- ImportError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Se lanza cuando una importación falla.
- Ejemplo:
import nonexistent_module
- IndexError:
- Ubicación: Ubicación: Hija de LookupError.
- Descripción: Descripción: Se produce cuando se accede a un índice fuera del rango de una secuencia.
- Ejemplo:
"lista = [1, 2, 3] lista[5]"
- KeyError:
- Ubicación: Ubicación: Hija de LookupError.
- Descripción: Descripción: Se lanza cuando una clave no se encuentra en un diccionario.
- Ejemplo:
"diccionario = {""a"": 1} diccionario[""b""]"
- KeyboardInterrupt:
- Ubicación: Ubicación: Hija de BaseException.
- Descripción: Descripción: Se genera cuando el usuario interrumpe la ejecución con Ctrl+C.
- Ejemplo:
"while True: pass # Interrumpe con Ctrl+C"
- MemoryError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Ocurre cuando el sistema no puede asignar memoria.
- Ejemplo:
a = [1] * (10**10)
- NameError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Se genera al usar una variable no definida.
- Ejemplo:
print(una_variable_no_definida)
- NotImplementedError:
- Ubicación: Ubicación: Hija de RuntimeError.
- Descripción: Descripción: Clase base para métodos no implementados.
- Ejemplo:
"class MiClase: def metodo(self): raise NotImplementedError(""Método no implementado"")"
- OSError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Clase base para errores relacionados con el sistema operativo.
- Ejemplo:
open("/ruta/no/existe.txt")
- TypeError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Se lanza al realizar una operación en un tipo inapropiado.
- Ejemplo:
x = "texto" + 5
- ValueError:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Se genera al usar un valor inválido.
- Ejemplo:
int("texto")
- UnicodeDecodeError:
- Ubicación: Ubicación: Hija de UnicodeError.
- Descripción: Descripción: Se lanza al decodificar caracteres Unicode incorrectos.
- Ejemplo:
b"\xff".decode("utf-8")
- StopIteration:
- Ubicación: Ubicación: Hija de Exception.
- Descripción: Descripción: Se lanza cuando un iterador no tiene más elementos.
- Ejemplo:
"iterador = iter([1, 2, 3]) next(iterador) next(iterador) next(iterador) next(iterador)"