Módulo 13 - Geradores e iteradores

Por que usar geradores

11 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 é avaliação preguiçosa (lazy).
  • Comparar o consumo de memória de um gerador e de uma lista.
  • Reconhecer quando um gerador é a escolha certa.
  • Trabalhar com sequências infinitas de forma segura, com limite.

A preguiça que vale a pena

Nas aulas anteriores você viu como criar geradores. Agora vem a pergunta que importa: por que se dar esse trabalho, se uma lista faz um serviço parecido? A resposta está em uma palavra, memória, e em um conceito, avaliação preguiçosa. Uma lista é ansiosa: no momento em que você a cria, todos os seus itens já existem na memória, todos de uma vez. Um gerador é preguiçoso: ele não guarda item nenhum de antemão; produz cada um só quando você o pede e o esquece assim que você passa para o próximo. Para dez itens isso não muda nada. Para dez milhões, muda tudo.

O exemplo abaixo mostra a diferença de forma concreta, usando o módulo sys para medir o tamanho de cada objeto na memória. A lista precisa reservar espaço para todos os números ao mesmo tempo. O gerador ocupa um punhado de bytes fixo, não importa quantos itens ele vá produzir, porque ele guarda apenas a receita de como produzi-los, não os itens em si. É a diferença entre carregar uma biblioteca inteira nas costas e ter uma ficha que busca um livro por vez.

import sys

lista = [x * x for x in range(1_000_000)]
gerador = (x * x for x in range(1_000_000))

print(sys.getsizeof(lista))     # cerca de 8000000 bytes
print(sys.getsizeof(gerador))   # cerca de 200 bytes

# Mesma sequencia de quadrados; consumo de memoria completamente diferente.

A lista reserva memória para um milhão de itens; o gerador ocupa um tamanho fixo minúsculo.

Sequências enormes e infinitas

A avaliação preguiçosa abre uma porta que a lista nunca poderia: representar sequências que não cabem na memória, ou que nem têm fim. Você não consegue criar a lista de todos os números pares, porque ela seria infinita e nenhuma memória a comportaria. Mas consegue escrever um gerador que os produz um a um, para sempre. Isso parece perigoso, e é, se você tentar consumir tudo. O segredo é sempre impor um limite no momento do consumo, pedindo apenas os itens de que precisa e parando ali.

def pares():
    n = 0
    while True:       # loop sem fim: gera pares para sempre
        yield n
        n = n + 2

g = pares()
for _ in range(5):
    print(next(g), end=" ")   # 0 2 4 6 8
print()

# O gerador e infinito, mas so pedimos 5 itens. Percorrer tudo travaria o programa.

Um gerador infinito é seguro enquanto você limita o consumo, aqui pedindo só cinco itens.

Vale ter honestidade sobre o outro lado da moeda. Geradores não são melhores que listas em toda situação. Como eles se esgotam depois de percorridos, você não pode voltar ao começo nem acessar o item de índice 500 direto: um gerador não tem g[500]. Se você precisa percorrer a mesma sequência várias vezes, ordenar, contar o total ou acessar itens por posição, a lista é a ferramenta certa. O gerador brilha quando o fluxo é de mão única: passar por cada item uma vez, do começo ao fim, sem precisar de todos ao mesmo tempo. É exatamente esse cenário que a prática final deste módulo vai explorar com um arquivo grande.

Amarrando os motivos

Resumindo os motivos: geradores economizam memória porque são preguiçosos, produzindo cada item sob demanda em vez de guardar todos; permitem representar sequências enormes ou até infinitas, desde que você limite o consumo; e deixam o programa começar a trabalhar com os primeiros itens antes de os últimos existirem. O custo é abrir mão do acesso aleatório e da possibilidade de reler. Sabendo dessa troca, você escolhe a estrutura certa para cada caso: lista quando precisa de tudo na mão, gerador quando basta passar por cada item uma vez.

Teste rápido

Por que um gerador de um milhão de itens ocupa muito menos memória que a lista equivalente?

Perguntas frequentes

O que é avaliação preguiçosa em uma frase?
É calcular cada valor só na hora em que ele é pedido, em vez de calcular tudo de antemão. Geradores são preguiçosos: produzem um item quando você o consome e nem um a mais, o que economiza memória e permite começar a usar os primeiros resultados antes de os últimos existirem.
Gerador é sempre melhor que lista?
Não. Gerador é melhor quando você percorre a sequência uma vez e não precisa de todos os itens ao mesmo tempo. Lista é melhor quando você precisa reler, ordenar, contar o total ou acessar itens por índice. A escolha depende do que você vai fazer com os dados.
Como um gerador pode ser infinito sem travar o programa?
Ser infinito é seguro enquanto você limita o consumo. O gerador só produz um item quando pedido, então, se você pede cinco itens e para, ele gera cinco e descansa. O perigo está em tentar consumir tudo, com list() ou um for sem parada, o que rodaria para sempre.
Posso acessar o item de índice 100 de um gerador?
Não diretamente: geradores não suportam g[100]. Eles entregam itens em ordem, um por vez, e se esgotam. Para chegar ao centésimo item você teria de percorrer os anteriores. Se precisa de acesso por posição, use uma lista ou a função islice do módulo itertools para pular itens.
Como limito com segurança um gerador infinito?
Você tem três caminhos comuns: percorrer dentro de um range com next(), usar um for com uma condição de break, ou usar itertools.islice(gerador, n) para pegar os primeiros n itens. Qualquer um deles impõe o limite no consumo, que é onde ele deve ficar.
Geradores ajudam com arquivos e dados de fora do programa?
Muito. Ler um arquivo enorme linha a linha, processar registros de um banco ou consumir uma resposta grande são casos ideais: você trata um item por vez, sem carregar tudo na memória. A prática final deste módulo mostra exatamente isso com um arquivo grande.

Fontes

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