7 Patrones de Manejo de Errores en Python que Todo Desarrollador Debe Conocer

Python es fácil de escribir, pero un manejo descuidado de errores puede convertir una aplicación funcional en una que falla silenciosamente en producción. Sin stack trace, sin alerta — solo una función que dejó de trabajar hace tres días y nadie lo notó.

Este artículo cubre 7 patrones de manejo de errores que realmente usarás en el trabajo. Cada fragmento funciona con Python 3.6+ estándar, sin dependencias.

Antes de empezar: el manejo de excepciones no es algo que se añade al final. Es parte del diseño. El mejor código no previene los errores — se asegura de que los errores no tumben el sistema.

1. try / except básico — Empieza aquí

La base de todo manejo de errores en Python. Captura tipos específicos de excepciones y responde a cada uno de manera diferente.

Python
def divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "No se puede dividir por cero"
    except TypeError:
        return "Por favor ingresa números"

print(divide(10, 2))    # 5.0
print(divide(10, 0))    # No se puede dividir por cero
print(divide(10, "a"))  # Por favor ingresa números

La regla de oro: nunca escribas un except: vacío. Siempre especifica el tipo de excepción.

El error clásico de principiante:

Python
# NO hagas esto
except Exception:
    pass

Esto silencia cada error. Como mínimo, registra la excepción:

Python
except ZeroDivisionError as e:
    logging.error("División fallida: %s", e)

Conclusión: Captura excepciones por tipo. Trata except: pass como una bomba de tiempo.

2. finally — Limpieza garantizada

Cuando abres un archivo, una conexión a base de datos o un socket, necesitas cerrarlo — sin importar si la operación tuvo éxito o no.

Python
f = None
try:
    f = open("data.txt", "w")
    f.write("hello")
except IOError as e:
    print("Escritura fallida:", e)
finally:
    if f:
        f.close()
    print("Limpieza completada")

El bloque finally se ejecuta pase lo que pase.

Error común: escribir f.close() sin verificar si f fue creado.

Historia de terror: olvidar cerrar un file handle en un proceso batch. El archivo queda bloqueado. A la mañana siguiente, el job programado falla.

Conclusión: Usa finally para limpieza. Siempre verifica que el recurso fue creado.

3. La sentencia with — Cómo lo hacen los profesionales

En la práctica, rara vez escribirás finally: f.close() a mano. La sentencia with lo hace automáticamente.

Python
try:
    with open("data.txt", "w") as f:
        f.write("hello")
except IOError as e:
    print("Escritura fallida:", e)

Puedes abrir múltiples archivos en una sola sentencia:

Python
with open("input.txt") as src, open("output.txt", "w") as dst:
    dst.write(src.read())

Conclusión: Manejo de archivos = with. Sin excepciones.

4. Re-lanzar excepciones con raise

A veces necesitas registrar un error y dejar que se propague.

Python
import logging

def process(data):
    try:
        return transform(data)
    except Exception as e:
        logging.error("Procesamiento fallido: %s", e)
        raise

Sin el raise, la función retorna None silenciosamente.

Para agregar contexto, usa encadenamiento de excepciones:

Python
class ProcessingError(Exception):
    pass

try:
    result = transform(data)
except ValueError as e:
    raise ProcessingError("Datos de entrada inválidos") from e

Conclusión: Log-and-swallow es una fábrica de bugs.

5. Excepciones personalizadas — Errores con significado

Las excepciones integradas son genéricas. Las excepciones personalizadas hacen tu código auto-documentado.

Python
class ValidationError(Exception):
    """Se lanza cuando los datos no pasan validación."""
    pass

class APIError(Exception):
    def __init__(self, status_code, message):
        self.status_code = status_code
        super().__init__(f"{status_code}: {message}")

def validate_age(age):
    if age < 0:
        raise ValidationError("La edad no puede ser negativa")
    return age

try:
    validate_age(-5)
except ValidationError as e:
    print(e)

Reglas prácticas:

• Siempre hereda de Exception, nunca de BaseException.
• Nombres descriptivos: PaymentDeclinedError supera a Error1.
• No crees docenas de excepciones para un proyecto pequeño.

Conclusión: Las excepciones personalizadas son herramientas de diseño.

6. assert — Verificaciones solo para desarrollo

assert verifica supuestos durante el desarrollo. Si la condición es falsa, lanza AssertionError.

Python
def withdraw(balance, amount):
    assert amount > 0, "El monto debe ser positivo"
    assert balance >= amount, "Fondos insuficientes"
    return balance - amount

print(withdraw(100, 50))  # 50
print(withdraw(100, 200)) # AssertionError

Lo crítico: assert puede ser deshabilitado con python -O.

Para validación en producción:

Python
def withdraw(balance, amount):
    if amount <= 0:
        raise ValueError("El monto debe ser positivo")
    if balance < amount:
        raise ValueError("Fondos insuficientes")
    return balance - amount

Conclusión: Assert es una herramienta de desarrollo, no un guardia de producción.

7. Lógica de reintentos — Porque las redes fallan

Las llamadas a API expiran. Las conexiones a BD se caen. El reintento no es opcional.

Python
import time
import random

def call_api():
    if random.random() < 0.7:
        raise ConnectionError("Servidor no disponible")
    return {"status": "ok"}

max_retries = 5
for attempt in range(max_retries):
    try:
        result = call_api()
        print("Éxito:", result)
        break
    except ConnectionError as e:
        wait = 2 ** attempt
        print(f"Intento {attempt + 1} falló, reintentando en {wait}s...")
        time.sleep(wait)
else:
    print("Todos los reintentos agotados")

Reglas para reintentos en producción:

Siempre establece un máximo de reintentos.
Usa backoff exponencial.
Solo reintenta errores transitorios.
• La construcción for/else es perfecta para esto.

Conclusión: Todo código que toque la red necesita reintentos. Punto.

Resumen: El manejo de errores es diseño

El kit completo:

1. try/except — Captura específica.
2. finally — Limpieza garantizada.
3. with — Gestión Pythónica de recursos.
4. raise — No tragues errores.
5. Excepciones personalizadas — Tipos auto-documentados.
6. assert — Solo para desarrollo.
7. Reintentos — Obligatorio para red.

El manejo de excepciones no se trata de prevenir errores — se trata de asegurar que los errores no tumben el sistema.

Comments

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *