lru_cache en Python: acelera funciones con caché

Publicado el: 11/07/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

    Uso do módulo random para gerar valores aleatórios em Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Módulo random en Python: guía práctica para principiantes

    Aprende el módulo random en Python: enteros, decimales, choice, sample, shuffle, seeds, distribuciones y diferencias con secrets.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Uso do módulo collections para estruturas avançadas em Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Módulo collections en Python: deque, Counter y más

    Aprende collections en Python con Counter, defaultdict, deque, namedtuple, ChainMap y buenas prácticas para elegir estructuras eficientes.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Introdução ao módulo itertools para iniciantes em Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    itertools en Python: guía práctica para iterar mejor

    Aprende itertools en Python con count, cycle, chain, islice, combinations, product, groupby, tee y zip_longest.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Manipulação moderna de arquivos usando pathlib em Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Pathlib en Python: rutas y archivos de forma moderna

    Aprende pathlib en Python para crear rutas, leer y escribir archivos, recorrer carpetas, filtrar extensiones, renombrar y organizar datos.

    Ler mais

    Tempo de leitura: 3 minutos
    11/07/2026
    Manipulação de arquivos e pastas usando módulo os em Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Módulo os en Python: archivos, carpetas y entorno

    Aprende el módulo os en Python para listar, crear, renombrar y eliminar archivos, recorrer carpetas, manejar rutas y leer variables

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Uso do Counter do collections para contar elementos em Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Counter en Python: contar elementos con collections

    Aprende Counter en Python para contar elementos, encontrar valores frecuentes, actualizar conteos, comparar resultados y analizar datos reales.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026