lru_cache: acelera funciones con caché

Actualizado el: 20/08/2026
Tempo de leitura: 5 minutos
Como acelerar código Python usando lru cache

functools.lru_cache es un decorador de la biblioteca estándar que memoriza el resultado de una función. Cuando la misma función vuelve a recibir los mismos argumentos, Python devuelve el valor guardado en lugar de repetir el cálculo. Esta técnica, conocida como memoización, puede acelerar algoritmos recursivos, consultas repetidas y transformaciones costosas con muy poco código.

Para comprender el patrón conviene conocer los decoradores en Python, la recursión, la medición con timeit y el análisis de cuellos de botella mediante cProfile.

El problema de calcular lo mismo varias veces

Una implementación recursiva ingenua de Fibonacci repite una gran cantidad de trabajo:

def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(35))

Cada llamada crea otras llamadas que vuelven a calcular valores ya conocidos. El tiempo crece rápidamente.

Añadir lru_cache

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(35))

La primera vez que se calcula fibonacci(20), el resultado queda almacenado. Las llamadas posteriores con el argumento 20 recuperan ese valor.

Qué significa LRU

LRU significa “Least Recently Used”, o menos recientemente usado. Cuando la caché alcanza su límite, elimina primero las entradas que llevan más tiempo sin utilizarse. Esto permite controlar el consumo de memoria.

@lru_cache(maxsize=128)
def procesar(valor):
    ...

El valor predeterminado de maxsize es 128. Con maxsize=None la caché puede crecer sin límite, por lo que solo debe utilizarse cuando el conjunto de argumentos posibles sea pequeño y conocido.

Los argumentos deben ser hashables

La caché utiliza los argumentos como claves. Por eso acepta enteros, cadenas, tuplas y otros objetos hashables, pero no listas o diccionarios mutables:

from functools import lru_cache

@lru_cache
def total(numeros):
    return sum(numeros)

print(total((10, 20, 30)))

Si necesitas recibir una lista, conviértela a tupla antes de llamar a la función o crea una capa externa:

@lru_cache
def _total_cache(numeros):
    return sum(numeros)


def total(numeros):
    return _total_cache(tuple(numeros))

Inspeccionar el rendimiento de la caché

cache_info() muestra aciertos, fallos, tamaño máximo y entradas actuales:

print(fibonacci.cache_info())

Un resultado puede verse así:

CacheInfo(hits=33, misses=36, maxsize=None, currsize=36)

Los hits indican llamadas resueltas desde la caché. Los misses representan cálculos reales.

Vaciar la caché

Cuando cambian los datos subyacentes, utiliza:

fibonacci.cache_clear()

En una aplicación real, borrar toda la caché puede ser costoso, pero evita devolver resultados obsoletos. Diseña una política clara de invalidación.

Parámetro typed

Con typed=True, algunos argumentos de tipos distintos se almacenan por separado:

@lru_cache(maxsize=64, typed=True)
def convertir(valor):
    return str(valor)

Esto puede ser relevante cuando 1, 1.0 y otros valores equivalentes deben producir comportamientos diferentes.

Ejemplo con lectura de configuración

from functools import lru_cache
import json
from pathlib import Path

@lru_cache(maxsize=1)
def cargar_configuracion():
    ruta = Path("config.json")
    return json.loads(ruta.read_text(encoding="utf-8"))

La lectura del disco ocurre una vez. Sin embargo, si el archivo cambia, la función seguirá devolviendo el objeto almacenado hasta llamar a cargar_configuracion.cache_clear().

No caches funciones con efectos secundarios

Una función que envía un correo, guarda una compra o registra un pago no debe decorarse con lru_cache. La segunda llamada podría no ejecutar la operación:

# No hacer esto
@lru_cache
def registrar_pago(pedido_id):
    guardar_en_base_de_datos(pedido_id)

La caché funciona mejor con funciones puras: el resultado depende únicamente de los argumentos y la función no modifica estado externo.

Cuidado con valores mutables devueltos

La caché devuelve el mismo objeto almacenado. Si el resultado es una lista y alguien la modifica, las llamadas futuras verán la modificación:

@lru_cache
def obtener_etiquetas():
    return ["python", "rendimiento"]

Prefiere tuplas, objetos inmutables o devuelve una copia desde una capa pública.

Usar cache cuando no necesitas límite

Python también ofrece functools.cache, equivalente a una caché sin límite:

from functools import cache

@cache
def factorial(n):
    if n < 2:
        return 1
    return n * factorial(n - 1)

Elige cache solo cuando el dominio de entradas esté controlado. Para entradas arbitrarias de usuarios, un límite evita crecimiento indefinido.

Caché en métodos de instancia

Al decorar un método, self forma parte de la clave:

from functools import lru_cache

class Conversor:
    @lru_cache(maxsize=32)
    def convertir(self, valor, tasa):
        return valor * tasa

La caché mantiene una referencia a la instancia mientras existan entradas, lo que puede prolongar su vida en memoria. En objetos de corta duración, evalúa otra estrategia.

lru_cache y concurrencia

La estructura interna es segura frente a actualizaciones concurrentes básicas, pero dos hilos pueden calcular el mismo valor simultáneamente si ambos llegan antes de que exista la entrada. No asumas que el decorador convierte una operación en un bloqueo de exclusión mutua.

Medir antes y después

from timeit import timeit

sin_calentar = timeit("fibonacci(35)", globals=globals(), number=1)
con_cache = timeit("fibonacci(35)", globals=globals(), number=1000)

print(sin_calentar)
print(con_cache)

Interpreta el resultado con cuidado: después de la primera llamada, las siguientes son aciertos de caché. Mide escenarios representativos y comprueba cache_info().

Cuándo evitar lru_cache

No lo uses si los resultados cambian constantemente, los argumentos tienen una cardinalidad enorme, el valor depende de hora, archivos o base de datos sin una invalidación clara, la función produce efectos secundarios o los resultados ocupan mucha memoria. Una caché incorrecta puede ser rápida y al mismo tiempo devolver datos equivocados.

La documentación oficial de lru_cache explica los parámetros, estadísticas y comportamiento en métodos. La documentación de functools.cache describe la alternativa sin límite.

Errores frecuentes

Los problemas comunes son pasar listas o diccionarios como argumentos, usar maxsize=None con entradas ilimitadas, cachear funciones con efectos secundarios, olvidar invalidar datos externos y devolver objetos mutables que después son modificados. Otro error es aplicar caché sin medir: si la función ya es rápida o casi nunca recibe argumentos repetidos, el beneficio puede ser mínimo.

Conclusión

lru_cache puede transformar una función repetitiva en una operación muy rápida, especialmente en recursión y cálculos puros. Define un tamaño razonable, utiliza argumentos hashables, inspecciona los aciertos y establece cuándo se vacía la caché. La memoización funciona mejor cuando la validez del resultado depende exclusivamente de los argumentos y el coste de recalcularlo es significativo.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Documento y bandeja de entrada que representan buzones de correo con mailbox en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox en Python: buzones de correo

    Aprende mailbox en Python para leer, crear y migrar Maildir, mbox y MH con locking, flags, mensajes y manejo seguro

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Editor de texto que representa formato con textwrap en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap en Python: formatea textos

    Aprende textwrap en Python para dividir, rellenar, acortar, indentar y quitar sangrías con control de ancho, espacios y palabras largas.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Carpeta y lupa que representan filtros de nombres con fnmatch en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch en Python: filtra nombres de archivos

    Aprende fnmatch en Python para filtrar nombres de archivos con comodines, controlar mayúsculas, excluir patrones y distinguir glob de regex.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor con datos binarios que representa arrays numéricos compactos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    array en Python: números compactos

    Aprende array en Python para almacenar números compactos, usar archivos binarios, byte order, memoryview y buffers seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Círculo cromático que representa conversiones RGB, HSV y HLS con colorsys en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys en Python: RGB, HSV y HLS

    Aprende colorsys en Python para convertir colores entre RGB, HSV, HLS y YIQ, crear paletas y evitar errores de escala

    Ler mais

    Tempo de leitura: 5 minutos
    09/08/2026
    Icono de configuración que representa archivos plist con plistlib en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib: lee y escribe archivos plist

    Aprende plistlib en Python para leer y escribir archivos plist XML y binarios, validar datos y manejar fechas, bytes y

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026