Por qué tu script Python es lento y cómo mejorarlo

Publicado el: 11/07/2026
Tempo de leitura: 5 minutos
Dicas para otimizar scripts Python lentos

Un script Python lento no siempre necesita una técnica avanzada. Muchas veces el problema está en un bucle innecesario, una estructura de datos incorrecta, demasiadas lecturas de disco, solicitudes de red repetidas o cálculos que se ejecutan una y otra vez. La forma profesional de mejorar el rendimiento consiste en medir, identificar el cuello de botella y cambiar solo lo que realmente importa.

Primero define qué significa lento

Antes de optimizar, establece un escenario reproducible. ¿El script tarda diez segundos, diez minutos o varias horas? ¿La lentitud aparece al iniciar, durante una consulta, al procesar archivos o al guardar resultados? Sin una medición inicial no podrás demostrar que una modificación mejoró el programa.

Medir con perf_counter

Para una medición sencilla:

from time import perf_counter

inicio = perf_counter()
ejecutar_tarea()
duracion = perf_counter() - inicio
print(f"Duración: {duracion:.3f} segundos")

Repite varias veces con la misma entrada. Una sola ejecución puede estar afectada por cachés, procesos del sistema o latencia externa.

Localizar cuellos de botella con cProfile

Cuando el programa contiene muchas funciones, utiliza un perfilador:

python -m cProfile -s cumulative mi_script.py

La salida indica cuántas veces se llamó cada función, el tiempo interno y el tiempo acumulado. La guía de cProfile en Python explica cómo interpretar estos valores. La documentación oficial de los perfiladores describe cProfile, profile y pstats.

Usar timeit para fragmentos pequeños

timeit sirve para comparar alternativas cortas evitando parte del ruido:

from timeit import repeat

resultados = repeat(
    "sum(range(1000))",
    repeat=5,
    number=10_000,
)
print(min(resultados))

No utilices un microbenchmark para concluir cómo se comportará una aplicación completa. La documentación oficial de timeit explica parámetros, línea de comandos y buenas prácticas.

Elegir la estructura de datos correcta

Buscar repetidamente en una lista puede ser costoso. Si solo necesitas comprobar pertenencia, un conjunto suele ser más apropiado:

usuarios_validos = {"ana", "luis", "marta"}

if nombre in usuarios_validos:
    autorizar()

Listas, conjuntos y diccionarios tienen propiedades diferentes. La guía de listas en Python ayuda a elegir una colección adecuada.

Evitar trabajo repetido dentro de bucles

Mueve fuera del bucle los cálculos que no cambian:

# menos eficiente
for producto in productos:
    impuesto = obtener_tasa_configurada()
    guardar(producto.precio * impuesto)

# mejor
impuesto = obtener_tasa_configurada()
for producto in productos:
    guardar(producto.precio * impuesto)

La mejora real depende del costo de obtener_tasa_configurada, pero el principio es evitar repetir trabajo constante.

Usar funciones incorporadas

Funciones como sum, min, max, any y all suelen ser claras y eficientes:

total = sum(venta.total for venta in ventas)
existe_error = any(item.error for item in resultados)

No reemplaces código legible por trucos difíciles de mantener solo por una diferencia mínima.

Comprensiones y generadores

Una comprensión puede ser más compacta que un bucle que solo construye una lista:

cuadrados = [n * n for n in numeros]

Si solo recorrerás los valores una vez, un generador evita almacenar toda la colección:

cuadrados = (n * n for n in numeros)
for valor in cuadrados:
    procesar(valor)

Esto es especialmente importante cuando existe riesgo de MemoryError.

Reducir operaciones de entrada y salida

Leer y escribir en disco suele ser mucho más lento que una operación en memoria. En lugar de abrir un archivo por cada línea, abre una sola vez:

with open("salida.txt", "w", encoding="utf-8") as archivo:
    for linea in lineas:
        archivo.write(linea + "\n")

Para bases de datos, agrupa inserciones y evita una consulta por elemento. Para APIs, considera paginación, reutilización de conexiones y límites de concurrencia.

No confundir CPU con espera

Una tarea intensiva en CPU necesita mejores algoritmos, bibliotecas optimizadas o procesos paralelos. Una tarea que espera red, disco o base de datos puede beneficiarse de concurrencia o asincronía. Aplicar asyncio a cálculos pesados no los vuelve automáticamente más rápidos.

Complejidad algorítmica

Un algoritmo cuadrático puede funcionar con cien elementos y colapsar con un millón:

# comparación repetida costosa
repetidos = []
for valor in datos:
    if datos.count(valor) > 1:
        repetidos.append(valor)

# enfoque más apropiado
from collections import Counter
conteos = Counter(datos)
repetidos = [valor for valor, cantidad in conteos.items() if cantidad > 1]

La biblioteca estándar ofrece soluciones preparadas para muchos patrones. El artículo sobre itertools muestra cómo construir iteraciones eficientes.

Caché con límites

Si una función pura repite cálculos con los mismos argumentos, lru_cache puede ayudar:

from functools import lru_cache

@lru_cache(maxsize=256)
def calcular_precio(codigo):
    return consulta_costosa(codigo)

La caché debe tener un límite y una estrategia coherente. Una caché ilimitada puede convertirse en un problema de memoria.

NumPy y bibliotecas compiladas

Para grandes operaciones numéricas, NumPy suele ejecutar bucles en código compilado. Evita aplicar una función Python elemento por elemento cuando exista una operación vectorizada. Sin embargo, medir sigue siendo necesario: crear muchas copias de arrays también tiene costo.

Python compilado y Cython

Cuando el cuello de botella está en cálculos Python que no pueden vectorizarse, Cython puede añadir tipos estáticos y compilar partes críticas. Revisa la guía para convertir Python con Cython. Esta opción añade complejidad de compilación y no debe ser el primer paso.

Funciones pequeñas y medibles

Separar el programa en funciones claras permite perfilar cada etapa, probar cambios y evitar efectos inesperados. Una función gigantesca dificulta saber dónde se consume el tiempo.

Errores frecuentes al optimizar

Los errores más comunes son optimizar sin medir, elegir siempre la ejecución más rápida de una prueba inestable, ignorar la base de datos, sacrificar legibilidad por microsegundos y aumentar concurrencia sin límites. También es un error comparar dos alternativas con entradas diferentes.

Flujo recomendado

Define una carga representativa, registra el tiempo, perfila el programa, selecciona el cuello de botella principal y formula una hipótesis. Haz un cambio pequeño, ejecuta pruebas de corrección y vuelve a medir. Conserva el cambio solo si aporta una mejora relevante sin deteriorar mantenimiento o memoria.

Un script rápido no nace de aplicar todos los trucos disponibles. Nace de eliminar el trabajo innecesario en la parte que realmente domina el tiempo total. Medir antes y después convierte la optimización en ingeniería, no en intuición.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Uso de dataclasses para simplificar classes em Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Dataclasses en Python: clases de datos más limpias

    Aprende dataclasses en Python: campos, valores predeterminados, field, frozen, order, __post_init__, herencia, asdict y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Criação de hashes seguros para senhas usando Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Hash de contraseñas con bcrypt en Python

    Aprende bcrypt en Python para almacenar contraseñas: salt, coste, hash, verificación, rehash, límites, errores y recomendaciones de seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Problemas de travamento com threading em scripts Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Threading en Python: evita que tus scripts se bloqueen

    Aprende threading en Python: hilos, tareas de entrada y salida, join, locks, colas, ThreadPoolExecutor, errores y límites del GIL.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Leitura de variáveis de ambiente em projetos Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Variables de entorno en Python: guía segura

    Aprende a usar variables de entorno en Python, archivos .env, validación, conversión de tipos, secretos, pruebas y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Introdução ao uso de type hints em Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Type hints en Python: escribe código más claro

    Aprende type hints en Python con funciones, colecciones, Optional, TypedDict, Callable, genéricos, Protocol y herramientas de análisis.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Medição de tempo de execução de código Python com timeit
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Timeit en Python: medir el rendimiento correctamente

    Aprende timeit en Python para comparar funciones, repetir benchmarks, separar setup, medir por operación y evitar errores de microoptimización.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026