Você já sentiu que seu programa está demorando uma eternidade para processar cálculos repetitivos? Sabia que existe uma forma mágica de memorizar os resultados e fazer seu script voar? Se você quer aprender como acelerar seu código Python com @lru_cache em 2 minutos, este artigo é o ponto de partida ideal. Muitas vezes, a lentidão não vem do hardware, mas sim da forma como estruturamos nossas funções em Python, especialmente quando lidamos com tarefas que processam os mesmos dados repetidamente.
O Python é uma linguagem extremamente versátil, mas por ser interpretada, pode apresentar gargalos em algoritmos pesados. No entanto, a biblioteca padrão do Python oferece ferramentas poderosas para resolver isso sem que você precise reescrever todo o seu projeto. O decorador @lru_cache, que faz parte do módulo functools, permite implementar uma técnica chamada “memoização”. Em termos simples, ele guarda o resultado de uma função para que, se você chamá-la novamente com os mesmos argumentos, o Python simplesmente devolva o valor salvo em vez de recalcular tudo.
O que é exatamente o @lru_cache?
O nome lru_cache é uma abreviação para Least Recently Used Cache (Cache do Menos Recentemente Utilizado). Imagine que você tem uma pequena estante para guardar livros. Quando a estante fica cheia e você precisa guardar um livro novo, você joga fora aquele que não toca há mais tempo. É exatamente isso que esse decorador faz com a memória do seu computador.
Ele é um dos decoradores Python mais úteis para quem busca performance. Ao aplicar essa ferramenta sobre uma função, você cria um mapa de entradas e saídas. Se a função for pura (ou seja, sempre retorna o mesmo resultado para os mesmos argumentos), o ganho de velocidade é absurdo, reduzindo o tempo de execução de vários segundos para milissegundos.
Por que seu código fica lento sem Cache?
Muitos programadores iniciantes enfrentam problemas de desempenho e acabam acreditando no mito de por que Python é lento. Na verdade, a lentidão geralmente ocorre em processos redundantes. Tomemos como exemplo o cálculo da sequência de Fibonacci de forma recursiva. Sem cache, o computador calcula o mesmo número milhares de vezes, gerando uma árvore de chamadas que cresce exponencialmente.
De acordo com a documentação oficial da Python Software Foundation, o uso de caches pode salvar recursos preciosos de CPU. Isso é vital em aplicações que fazem consultas em bancos de dados, chamadas de rede ou cálculos matemáticos complexos. Quando você não utiliza uma estratégia de cache, está basicamente pedindo ao processador para “redescobrir a roda” a cada segundo.
Configurando o ambiente para usar functools
A melhor parte é que você não precisa instalar nada externo. O lru_cache faz parte do módulo built-in functools. Se você já tem o Python instalado, está pronto para começar. Se você ainda tem dúvidas sobre como configurar sua máquina, confira nosso guia de instalação e configuração do VS Code para facilitar seu fluxo de trabalho.
Para usar o recurso, basta realizar a importação no topo do seu arquivo script:
from functools import lru_cacheCom essa linha, você desbloqueia uma das otimizações mais famosas da comunidade de desenvolvimento. Vamos ver como aplicar isso na prática agora.
Passo a passo: Como acelerar seu código Python com @lru_cache em 2 minutos
Vamos aplicar o conceito em um problema clássico de recursão em Python. Sem o cache, calcular o 35º termo da sequência de Fibonacci pode levar alguns segundos. Com o cache, é instantâneo.
Etapa 1: O problema sem otimização
Nesta etapa, temos apenas uma função comum que se chama repetidamente. O processador fica sobrecarregado pois não “lembra” dos cálculos anteriores.
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Isso vai demorar visivelmente para n > 30
print(fibonacci(35))Etapa 2: Aplicando o @lru_cache
Agora, basta adicionar o decorador acima da definição da função. Por padrão, no Python 3.8+, você pode usar apenas @lru_cache sem parênteses, ou definir um tamanho máximo para o cache.
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(35)) # Resultado instantâneo!Note que o parâmetro maxsize define quantos resultados diferentes o Python deve guardar. Se você usar maxsize=None, o cache poderá crescer sem limites, o que deve ser usado com cuidado para evitar um MemoryError em Python.
Analisando o ganho de performance
Para provar que o método funciona, você pode medir o tempo do código com timeit ou o módulo time. Em testes típicos, uma função que leva 10 segundos para ser executada recursivamente passa a levar 0.0001 segundos após a memorização do primeiro resultado.
Isso acontece porque a complexidade algorítmica muda radicalmente. No caso do Fibonacci, saímos de uma complexidade exponencial (muito ruim) para uma complexidade linear (muito boa). É o tipo de otimização que separa desenvolvedores juniores de profissionais sêniores.
Quando NÃO usar o @lru_cache?
Apesar de parecer uma bala de prata, o @lru_cache tem limitações importantes. Você não deve usá-lo em funções que:
- Retornam valores diferentes para a mesma entrada (como funções que geram números aleatórios ou pegam a hora atual).
- Têm efeitos colaterais (como escrever em um arquivo ou alterar um objeto global).
- Recebem argumentos que não podem ser “hasheáveis”, como listas ou dicionários. O cache precisa que os argumentos sejam imutáveis, como inteiros, strings em Python ou tuplas.
Se você tentar passar uma lista como argumento para uma função com cache, o Python retornará um erro interno, pois ele não consegue criar uma chave única para esse objeto mutável.
Limpando e monitorando o Cache
Em sistemas de produção, às vezes você precisa limpar o cache manualmente para liberar memória ou forçar um novo cálculo. O decorador fornece um método chamado cache_clear().
fibonacci.cache_clear()Além disso, você pode usar fibonacci.cache_info() para ver estatísticas de quanto o seu cache está sendo efetivo. Ele mostrará o número de “hits” (vezes que o valor foi pego do cache) e “misses” (vezes que o cálculo teve que ser feito).
Exemplo prático: Cache em chamadas de API
Imagine que você está criando um sistema que precisa consumir APIs REST com Python. Se a API retorna dados que mudam pouco (como a cotação do dólar que você quer checar a cada hora), o cache pode evitar chamadas excessivas e economizar sua franquia de dados ou evitar bloqueios por excesso de requisições.
import requests
from functools import lru_cache
@lru_cache(maxsize=10)
def obter_dados_da_api(url):
print(f"Fazendo requisição real para {url}")
resposta = requests.get(url)
return resposta.json()
# Na primeira vez, ele faz a requisição
dados = obter_dados_da_api("https://api.exemplo.com/v1/precos")
# Na segunda vez (dentro do limite do cache), ele não acessa a internet
dados_novamente = obter_dados_da_api("https://api.exemplo.com/v1/precos")Diferença entre @lru_cache e @cache
A partir do Python 3.9, foi introduzido o @cache. Ele é basicamente um apelido para @lru_cache(maxsize=None). Enquanto o lru_cache é mais seguro por permitir limitar o uso da memória, o @cache é mais simples de escrever quando você tem certeza de que os dados não vão esgotar a RAM do servidor.
Para quem trabalha com análise de dados pesada usando Numpy ou Pandas, entender essa gestão de memória é fundamental para criar scripts otimizados que rodam em ambientes limitados, como containers Docker.
Código Completo do Projeto
Aqui está um script completo que você pode copiar e testar no seu computador para ver a diferença de performance ao vivo. Ele compara a execução com e sem a otimização.
import time
from functools import lru_cache
# Versão lenta (sem cache)
def fib_lento(n):
if n < 2:
return n
return fib_lento(n-1) + fib_lento(n-2)
# Versão rápida (com lru_cache)
@lru_cache(maxsize=None)
def fib_rapido(n):
if n < 2:
return n
return fib_rapido(n-1) + fib_rapido(n-2)
# Testando a versão rápida
print("--- Testando Fib(35) COM lru_cache ---")
inicio = time.time()
print(f"Resultado: {fib_rapido(35)}")
fim = time.time()
print(f"Tempo levado: {fim - inicio:.6f} segundos")
# Testando a versão lenta
print("\n--- Testando Fib(35) SEM lru_cache ---")
inicio = time.time()
print(f"Resultado: {fib_lento(35)}")
fim = time.time()
print(f"Tempo levado: {fim - inicio:.6f} segundos")
# Verificando estatísticas do cache
print(f"\nEstatísticas do Cache: {fib_rapido.cache_info()}")Ao rodar o código acima, você verá que a diferença é brutal. Aprender como acelerar seu código Python com @lru_cache em 2 minutos é apenas o começo de uma jornada de otimização. Sempre que identificar funções que realizam cálculos pesados ou acessos frequentes a recursos externos de forma repetitiva, considere aplicar esta técnica. Se o seu código lida com grandes volumes de informação, você também pode se interessar em como ler arquivos gigantes sem travar, combinando essas estratégias para uma performance de elite.
Perguntas Frequentes
O @lru_cache gasta muita memória?
Depende do valor que você define em maxsize. Se for None, o cache pode crescer indefinidamente. Se você definir um número fixo (ex: 128), ele manterá apenas os últimos 128 resultados, o que é muito seguro para a maioria das aplicações.
Posso usar @lru_cache em métodos de classes?
Sim, mas tome cuidado. Se você usar o decorador comum em um método de instância, o cache pode manter o objeto self na memória, impedindo que ele seja coletado pelo lixeiro (Garbage Collector). Para métodos, às vezes é melhor usar bibliotecas específicas ou criar uma lógica de cache manual.
O cache funciona entre execuções diferentes do script?
Não. O @lru_cache é um cache em memória (RAM). Uma vez que você encerra o programa, os dados são perdidos. Para um cache persistente, você precisaria de um banco de dados como Redis ou salvar em um arquivo local.
Posso usar listas como argumentos em funções com cache?
Não diretamente. Argumentos de funções “cacheáveis” devem ser imutáveis. Como uma lista pode ser alterada, o Python não permite usá-la. Converta a lista em uma tupla antes de passar para a função se precisar de cache.
Qual a diferença entre @lru_cache e memoização manual?
A memoização manual geralmente usa um dicionário em Python para guardar resultados. O @lru_cache é mais eficiente porque já implementa a lógica de expulsar resultados antigos (LRU) de forma otimizada em C.
O cache funciona com funções assíncronas (asyncio)?
O lru_cache padrão não é compatível com corrotinas de forma eficiente. Para isso, existem bibliotecas específicas como async-lru que lidam com funções async def.
Como saber se o cache está realmente funcionando?
Você pode usar o método funcao.cache_info(). Se o número de “hits” estiver aumentando conforme você chama a função com os mesmos parâmetros, o cache está funcionando perfeitamente.
Usar cache pode esconder bugs?
Sim. Se a sua função depende de variáveis globais que mudam, o cache pode retornar um valor antigo (“sujo”). Certifique-se de usar cache apenas em funções puras, onde a saída depende exclusivamente dos argumentos de entrada.







