Uma função que pausa
Um gerador parece com uma função normal, mas em vez de computar um resultado inteiro e retornar, ele produz um valor de cada vez, pausando entre as entregas até quem está pedindo querer o próximo valor.
O mais simples possível:
Repare yield em vez de return. A primeira vez que o for pede um valor, o Python roda o corpo da função até bater em yield 1. A função pausa exatamente ali, entrega 1 de volta ao laço e lembra exatamente onde parou — variáveis e tudo. A próxima iteração pega de onde parou: current += 1, volta para o while, yield 2. E assim por diante até a condição do laço falhar, quando o gerador simplesmente para.
Esse pausa-e-retoma é o truque inteiro.
Por que não só construir uma lista?
Porque a versão lista aloca todos os valores de antemão:
Tranquilo para 5 itens. Agora imagine que você quer 50 milhões de inteiros e só se importa com o primeiro que bate em alguma condição. A versão lista aloca 50 milhões de ints e aí você joga fora a maioria. A versão geradora cria exatamente tantos quantos quem consome usa. Quando o laço for encontra o que quer e dá break, o gerador simplesmente para.
Esse é o padrão que vale internalizar: geradores permitem escrever código de iteração sem decidir de antemão quanto do resultado você vai precisar.
Expressões geradoras
Se você escreveu uma list comprehension, já conhece a sintaxe — troque colchetes por parênteses:
squares_gen não computa nada ainda. É só uma receita. Iterar executa a receita um passo de cada vez.
Expressões geradoras são perfeitas como argumentos para funções que consomem um iterável:
Sem lista intermediária. sum, max e any leem valores um de cada vez, que é exatamente o que querem.
Lendo um arquivo grande, linha por linha
Esse é o caso canônico do mundo real para geradores — processar um arquivo grande demais para carregar na memória:
def parse_log_lines(path):
with open(path) as f:
for line in f:
if line.startswith("ERROR"):
yield line.rstrip()
for error in parse_log_lines("app.log"):
print(error)
O arquivo é lido preguiçosamente. Cada chamada ao gerador puxa uma linha do disco, filtra e entrega. O uso de memória fica estável independente do tamanho do arquivo.
Uma passada só
Um gerador tem uma única passagem. Depois que você iterou até o fim, ele está esgotado:
O segundo laço não imprime nada. O gerador não tem mais nada.
Se você precisa iterar mais de uma vez, ou chame a função geradora de novo para um gerador fresco, ou materialize a sequência com list(...) e itere a lista repetidamente. Escolha com base no custo: reconstruir está bem se o trabalho é barato; uma lista está bem se a sequência é pequena.
next() e iteração manual
Você não precisa usar um laço for. next() puxa um valor de cada vez:
StopIteration é como um gerador sinaliza "terminei". Laços for pegam silenciosamente. Em código manual, você pode passar um default para next(gen, default) para evitar a exceção.
Geradores infinitos
Como valores são produzidos sob demanda, um gerador pode representar uma sequência sem fim — contanto que quem consome pare de pedir:
while True com um yield dentro não trava o programa — só significa "se alguém continuar pedindo, continue produzindo". Quem consome decide quando parar.
Esse padrão aparece em dados de streaming, event loops e em qualquer lugar onde você puxa valores de uma fonte que não tem tamanho definido.
yield from: delegando a outro iterável
Se seu gerador quer entregar todo valor de outro iterável, yield from faz numa linha:
Sem yield from você escreveria um laço for aninhado com yield x dentro. Também encaminha chamadas send() e throw() corretamente se você usar — mas no dia a dia, pense nele como "entregue cada valor desta coisa".
Quando recorrer a um gerador
Três sinais de que um gerador é a ferramenta certa:
- A sequência é grande, possivelmente infinita, ou cara de produzir por inteiro.
- Quem consome pode parar antes do fim (um
breakna primeira combinação, por exemplo). - Você quer encadear transformações — filtro, map, take — sem construir listas intermediárias.
E quando não:
- Você precisa de acesso aleatório (
seq[42]). Geradores só vão para frente. - Você precisa iterar a mesma sequência várias vezes. Use uma lista.
- A sequência é pequena e você já a tem. Uma list comprehension é mais simples.
Geradores, list comprehensions e listas simples são cada um a resposta certa para trabalhos diferentes. A habilidade é escolher um sem pensar muito — e o jeito mais rápido de desenvolver esse instinto é notar, para cada iteração que você escreve, se "produzir tudo primeiro" ou "produzir um de cada vez" encaixa melhor.
Próxima: context managers em profundidade
Você já viu a maior parte dos idiomas que o Python usa para iteração. Context managers — a instrução with — vêm a seguir, e combinam bem com geradores para streaming de dados de arquivos e conexões de rede.
Perguntas frequentes
O que é um gerador em Python?
Um gerador é uma função que produz valores um de cada vez, pausando entre eles. Você escreve com def como função normal, mas usa yield em vez de return. Chamá-la retorna um objeto gerador; cada iteração de for ou cada chamada a next() roda a função até o próximo yield.
Qual a diferença entre uma lista e um gerador?
Uma lista guarda todo elemento em memória ao mesmo tempo. Um gerador computa elementos sob demanda e esquece depois que são consumidos. Para sequências grandes ou infinitas, geradores usam uma quantidade pequena e fixa de memória; para resultados pequenos que você precisa repetidamente, uma lista é melhor.
Posso iterar um gerador duas vezes?
Não. Um gerador é esgotado depois da primeira passada completa — um segundo laço for sobre ele não produz nada. Se você precisa iterar mais de uma vez, chame a função geradora de novo para pegar um gerador fresco, ou materialize os resultados numa lista.