Módulo 4 - Programação funcional

lru_cache: memoização sem esforço

9 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Entender o que é memoização e por que ela acelera código.
  • Aplicar lru_cache a uma função pura cara com um decorador.
  • Ver o ganho dramático em recursões como Fibonacci.
  • Reconhecer quando o cache ajuda e quando atrapalha.

O que é memoização

Algumas funções são caras de calcular e acabam sendo chamadas várias vezes com os mesmos argumentos. Recalcular tudo de novo é desperdício. A memoização resolve isso guardando os resultados: na primeira chamada com certos argumentos, a função calcula e guarda; nas próximas com os mesmos argumentos, ela devolve o valor guardado na hora. É uma troca de memória por tempo. Só funciona bem com funções puras, porque o resultado precisa depender apenas dos argumentos para o cache ser confiável.

Fazer isso à mão dá trabalho: você precisaria criar um dicionário, checar se o argumento já está lá, guardar o resultado, e ainda cuidar do tamanho do cache. O functools oferece tudo pronto no decorador lru_cache. Você o coloca acima da função e ela passa a memoizar sozinha, sem mudar o corpo. O nome LRU vem de least recently used: quando o cache atinge o limite, ele joga fora os resultados usados há mais tempo, mantendo os mais recentes.

from functools import lru_cache

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

print(fib(30))   # 832040
print(fib(50))   # 12586269025  (instantaneo com cache)

# Sem o cache, fib(50) recalcularia bilhoes de vezes os mesmos valores.

@lru_cache memoiza fib: cada valor é calculado uma vez, tornando a recursão viável.

O ganho dramático em recursões

O exemplo do Fibonacci recursivo mostra o poder da memoização. Sem cache, calcular o termo 50 é inviável na prática, porque a função recalcula os mesmos valores um número gigantesco de vezes, ramificando a cada chamada. Com o lru_cache, cada termo é calculado uma única vez e reaproveitado, e a mesma função que travava passa a responder instantaneamente. A lógica não mudou; só o decorador foi adicionado. Esse é o tipo de ganho que faz a memoização valer tanto a pena em cálculos repetitivos e caros.

Fibonacci sem cache

  • Recalcula os mesmos termos incontáveis vezes
  • O tempo explode conforme n cresce
  • fib(50) trava na prática
  • Muito trabalho desperdiçado

Fibonacci com lru_cache

  • Calcula cada termo uma única vez
  • O tempo cresce de forma suave
  • fib(50) responde na hora
  • Um decorador resolve, sem mudar a lógica

O parâmetro maxsize controla o tamanho do cache. Passar None deixa o cache crescer sem limite, o que é ótimo para uma recursão finita como o Fibonacci, mas perigoso num programa longo com muitos argumentos diferentes, porque a memória só cresce. Definir um limite, por exemplo alguns milhares de entradas, faz o cache descartar os resultados mais antigos quando enche. A escolha depende do padrão de uso: cache pequeno e limitado para uso contínuo, cache livre para cálculos pontuais e delimitados.

Quando o cache atrapalha

A memoização não é gratuita nem universal. Ela só é segura com funções puras: se o resultado depende de algo externo que muda, como a hora atual ou um arquivo, o cache devolveria um valor velho e errado. Ela consome memória, que cresce com a variedade de argumentos, então um cache sem limite pode virar um vazamento. E ela exige que os argumentos sejam hasheáveis, ou seja, imutáveis o bastante para virar chave: listas, por exemplo, não servem de argumento de uma função memoizada. Fora desses casos, é uma das otimizações mais fáceis que existem.

Fechando o functools, o lru_cache é o exemplo perfeito de como o estilo funcional e o desempenho se encontram. Ele só funciona porque a função é pura, o que reforça o valor da pureza que abriu o módulo. E ele resolve um problema real com uma linha. A próxima aula muda de assunto dentro do mesmo espírito e apresenta o itertools, uma caixa de ferramentas de iteradores que produz sequências, combina e fatia dados de forma preguiçosa e eficiente.

Teste rápido

Por que o lru_cache só deve ser usado com funções puras?

Perguntas frequentes

O que significa o LRU no nome lru_cache?
LRU vem de least recently used, ou menos usado recentemente. Quando o cache atinge o tamanho máximo, ele descarta os resultados que não são acessados há mais tempo, abrindo espaço para os novos. Isso mantém em memória os valores mais úteis e evita que o cache cresça sem controle.
Preciso mudar o corpo da função para memoizar?
Não. Essa é a beleza do lru_cache: você só adiciona o decorador acima da função e ela passa a memoizar sozinha, sem alterar a lógica interna. A função continua sendo escrita de forma normal; o cache é acrescentado por fora, o que deixa fácil ligar e desligar a otimização.
Por que não posso passar uma lista para uma função memoizada?
Porque o cache usa os argumentos como chave, e chaves precisam ser hasheáveis, o que exige imutabilidade. Listas e dicionários são mutáveis e não servem de chave. Se precisar passar uma coleção, use uma tupla no lugar da lista, que é imutável e funciona como argumento memoizável.
O cache sem limite pode causar problema?
Pode, num programa de longa duração com muitos argumentos diferentes, porque a memória do cache só cresce. Para recursões finitas e cálculos pontuais, o cache livre é seguro. Em uso contínuo, defina um maxsize para limitar o tamanho, deixando o mecanismo LRU descartar o que é usado há mais tempo.
Existe uma versão mais simples do lru_cache?
Sim. O functools oferece um decorador de cache sem limite de tamanho, mais direto, para quando você quer memoização simples e não se importa com o descarte por uso. Para a maioria dos casos didáticos, porém, o lru_cache com maxsize=None já resolve e deixa explícito o comportamento.
Dá para ver quantas vezes o cache foi aproveitado?
Dá. Uma função decorada com lru_cache ganha um método que informa estatísticas do cache, como quantas chamadas acertaram um valor guardado e quantas tiveram de calcular. Isso ajuda a avaliar se a memoização está valendo a pena naquele caso. A documentação oficial mostra como consultar.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.