Módulo 7 - Geradores e iteradores avançados

Generator expressions e a memória

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

O que você vai aprender

  • Escrever uma generator expression trocando colchetes por parênteses.
  • Entender a diferença de memória entre lista e gerador.
  • Saber que um gerador só pode ser percorrido uma vez.
  • Reconhecer quando usar cada um.

Só trocar colchetes por parênteses

Você já conhece a list comprehension do Intermediário, aquela expressão entre colchetes que constrói uma lista. A generator expression é irmã dela, com uma única diferença de escrita: troca os colchetes por parênteses. O efeito, porém, muda tudo. A list comprehension calcula todos os valores de uma vez e os guarda em uma lista completa na memória. A generator expression não calcula nada de imediato; ela devolve um gerador que produzirá cada valor só quando pedido.

quadrados_lista = [n * n for n in range(5)]   # colchetes: lista pronta
print(quadrados_lista)   # [0, 1, 4, 9, 16]

quadrados_ger = (n * n for n in range(5))     # parenteses: gerador
print(quadrados_ger)     # <generator object ...>
print(list(quadrados_ger))  # [0, 1, 4, 9, 16] quando consumido

Mesma lógica; colchetes criam a lista, parênteses criam um gerador preguiçoso.

No exemplo, a list comprehension já é a lista com os cinco quadrados. A generator expression é um objeto gerador que ainda não calculou nada; ele produz os valores conforme você os consome, seja com list, seja em um for. Para cinco números a diferença é irrelevante. A questão aparece quando os dados são grandes, e é aí que a economia de memória do gerador faz diferença de verdade.

A economia de memória em números

Suponha que você queira somar os quadrados de dez milhões de números. Com a list comprehension, o Python cria uma lista com dez milhões de itens na memória só para depois somá-los, o que consome centenas de megabytes à toa. Com a generator expression, nenhum item é guardado: cada quadrado é produzido, somado e descartado, um de cada vez. O uso de memória fica praticamente constante, independentemente do tamanho da entrada. É a diferença entre caber e estourar a memória.

# Pesado: cria uma lista de 10 milhoes de itens so para somar
total = sum([n * n for n in range(10_000_000)])

# Leve: nao guarda nada; soma um quadrado por vez
total = sum(n * n for n in range(10_000_000))
# Quando a generator expression e o unico argumento,
# os parenteses extras nem sao necessarios.

Com sum, o gerador soma sem materializar a lista inteira: memória quase constante.

AspectoList comprehensionGenerator expression
SintaxeColchetes [ ]Parênteses ( )
Quando calculaTudo de uma vezUm item por vez, sob demanda
Uso de memóriaProporcional ao tamanhoPraticamente constante
Pode reusar?Sim, é uma listaNão, esgota após um percurso

Lista contra gerador: mesma lógica, comportamentos de memória opostos.

O gerador se esgota: consumo único

Há um preço na economia, e ignorá-lo causa bugs sutis. Um gerador pode ser percorrido uma só vez. Depois de consumido, ele fica vazio: um segundo for sobre o mesmo gerador não produz nada, sem erro nenhum, o que confunde. A lista, por guardar tudo, pode ser percorrida quantas vezes você quiser. Então a decisão é clara: se você precisa dos valores mais de uma vez, use uma lista; se vai percorrer só uma vez, especialmente com dados grandes, prefira o gerador.

ger = (n for n in range(3))
print(list(ger))   # [0, 1, 2]  primeiro percurso consome tudo
print(list(ger))   # []          o gerador ja esta esgotado

# Uma lista pode ser percorrida varias vezes:
lst = [n for n in range(3)]
print(list(lst))   # [0, 1, 2]
print(list(lst))   # [0, 1, 2]  continua disponivel

Percorrer o gerador uma segunda vez devolve vazio; a lista permanece reutilizável.

Teste rápido

Qual é a diferença central entre (n*n for n in dados) e [n*n for n in dados]?

Perguntas frequentes

Quando os parênteses da generator expression são opcionais?
Quando a generator expression é o único argumento de uma função, como em sum(n*n for n in dados). Nesse caso, os parênteses da chamada já servem, e você não precisa de um par extra. Se houver outros argumentos, ou se quiser guardar o gerador em uma variável, aí os parênteses próprios são necessários.
Gerador é sempre melhor que lista?
Não. O gerador ganha em memória quando você percorre uma vez só e os dados são grandes. Mas se você precisa dos valores mais de uma vez, ou de índice, len e fatiamento, a lista é a escolha certa, porque o gerador não oferece nada disso e se esgota após um percurso. Cada um tem seu momento.
Por que o segundo for sobre um gerador não devolve nada?
Porque o gerador guarda uma posição que só avança e, uma vez percorrido até o fim, fica esgotado. Não há erro; ele simplesmente não produz mais valores. É uma pegadinha comum: se você precisa percorrer duas vezes, materialize em uma lista com list(gerador) ou recrie o gerador.
Dá para filtrar dentro de uma generator expression?
Dá, com um if no fim, igual à list comprehension: (n for n in dados if n > 0). O filtro também é preguiçoso, aplicado item a item conforme você consome. Isso permite encadear filtros e transformações sem materializar nenhuma etapa intermediária, tema da próxima aula sobre pipelines.
O gerador é mais rápido que a lista?
Depende do uso. Ele evita o custo de construir a lista inteira, o que ajuda quando você não precisa de todos os valores ao mesmo tempo. Mas a velocidade de produzir cada item é parecida. O ganho principal do gerador é memória, não uma corrida de velocidade; muitas vezes os dois têm desempenho de tempo semelhante.

Fontes

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