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.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: lru_cache: memoização sem esforço.
Os objetivos desta aula. 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.
Veja o essencial, parte por parte.
O que é memoização. Memoização guarda resultados de uma função para não recalcular os mesmos argumentos.
O ganho dramático em recursões. O exemplo do Fibonacci recursivo mostra o poder da memoização.
Quando o cache atrapalha. A função é pura? Se depende de estado externo mutável, o cache mente.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
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.