Generators com yield: Iteração Preguiçosa de Forma Simples
Ao trabalhar com grandes volumes de dados, carregar tudo na memória muitas vezes é desnecessário e ineficiente. Generators em Python resolvem isso produzindo valores um de cada vez em vez de construir coleções inteiras antecipadamente.
A chave para esse comportamento é a palavra-chave yield. Ela transforma uma função comum em um iterador preguiçoso.
O Que É um Generator?
Um generator é uma função que usa yield em vez de return para produzir uma sequência de valores ao longo do tempo.
Ao contrário das listas, generators não armazenam todos os valores na memória. Eles geram cada valor apenas quando solicitado.
Considere esta list comprehension:
nums = [n for n in range(1_000_000)]
Isso cria um milhão de inteiros imediatamente na memória.
Agora compare com uma generator expression:
nums = (n for n in range(1_000_000))
Isso produz números apenas quando iterado.
Como yield Funciona
Quando o Python encontra yield, ele:
- Retorna o valor atual
- Pausa a função
- Salva seu estado interno
- Retoma a execução na próxima iteração
Exemplo:
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
for num in count_up_to(5):
print(num)
Cada iteração retoma exatamente de onde parou. Nenhuma memória extra é alocada para valores não utilizados.
Por Que Generators São Importantes
O principal benefício é a avaliação preguiçosa. Os dados são gerados apenas quando necessário.
Isso é especialmente útil para:
- Processar arquivos grandes
- Trabalhar com streams de banco de dados
- Lidar com respostas de APIs
- Construir pipelines de dados
Em fluxos de ETL, generators permitem que cada etapa de transformação consuma e produza dados progressivamente. Isso mantém a memória estável e melhora a composição do código.
Encadeando Generators
Generators podem ser compostos para formar pipelines:
def read_lines(file):
with open(file) as f:
for line in f:
yield line.strip()
def filter_lines(lines):
for line in lines:
if "ERROR" in line:
yield line
for log in filter_lines(read_lines("system.log")):
print(log)
Cada função processa os dados de forma preguiçosa. Nada é carregado completamente na memória.
Boas Práticas
- Use generators para fontes de dados grandes ou infinitas
- Prefira generator expressions para transformações simples
- Evite misturar lógica de estado complexa dentro de generators
- Lembre-se de que generators só podem ser iterados uma vez
- Utilize funções como
next(),any()esum()de forma eficaz
Consideração Final
Generators não são apenas uma técnica de otimização de memória. Eles incentivam uma mentalidade de processamento em fluxo e modularidade.
Quando você precisa de iteração controlada e incremental, yield oferece uma solução limpa e expressiva.