Módulo 7 - Geradores e iteradores avançados
Pipelines preguiçosos e geradores infinitos
11 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Encadear geradores para formar um pipeline de processamento.
- Entender por que o pipeline não guarda etapas inteiras na memória.
- Criar um gerador infinito com segurança.
- Usar itertools.islice para consumir só uma parte de um gerador infinito.
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: Pipelines preguiçosos e geradores infinitos.
Os objetivos desta aula. Encadear geradores para formar um pipeline de processamento. Entender por que o pipeline não guarda etapas inteiras na memória. Criar um gerador infinito com segurança. Usar itertools.islice para consumir só uma parte de um gerador infinito.
Veja o essencial, parte por parte.
Encadeando geradores em um pipeline. Um pipeline encadeia geradores: a saída de um é a entrada do próximo.
Geradores que nunca acabam. Nunca chame list() ou sum() direto em um gerador infinito: trava o programa.
islice: cortar o infinito com segurança. A ferramenta certa para pegar só um pedaço de um gerador, inclusive infinito, é o itertools.islice.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Encadeando geradores em um pipeline
Um dos usos mais elegantes de geradores é montar um pipeline: uma linha de montagem em que cada gerador faz uma transformação e passa o resultado ao próximo. Imagine processar um arquivo enorme: um gerador lê as linhas, outro descarta as vazias, outro converte cada linha em número, e um último filtra os positivos. Cada etapa é um gerador que consome do anterior e produz para o seguinte, tudo preguiçosamente, item a item.
def ler(linhas):
for linha in linhas:
yield linha.strip()
def nao_vazias(linhas):
for linha in linhas:
if linha:
yield linha
def como_numero(linhas):
for linha in linhas:
yield int(linha)
def positivos(numeros):
for n in numeros:
if n > 0:
yield n
bruto = ["10", "", "-3", " 7 ", "", "42"]
# Encadeia as etapas: a saida de uma alimenta a proxima
pipeline = positivos(como_numero(nao_vazias(ler(bruto))))
print(list(pipeline)) # [10, 7, 42]Quatro geradores encadeados formam um pipeline; os dados fluem item a item.
O detalhe crucial é que, ao chamar list no fim, nenhum item intermediário completo existe na memória. Quando o pipeline precisa de um valor, ele puxa um item da última etapa, que puxa da anterior, e assim por diante até a fonte, um por vez. Nunca há uma lista inteira de linhas limpas, nem uma lista inteira de números; só um item viajando pelas etapas de cada vez. Isso permite processar arquivos maiores que a memória disponível.
Geradores que nunca acabam
Como o gerador só produz um valor quando pedido, ele não precisa ter fim. Um gerador com um while True que sempre cede um próximo valor é perfeitamente válido: ele representa uma sequência infinita, mas só calcula até onde você consumir. Isso seria impossível com uma lista, que precisaria caber inteira na memória. O cuidado é nunca tentar percorrer um gerador infinito por completo, com list ou um for sem saída, porque aí o programa nunca para.
def naturais():
n = 0
while True: # sequencia infinita
yield n
n += 1
def pares(numeros):
for n in numeros:
if n % 2 == 0:
yield n
# CUIDADO: nao faca list(naturais()) - nunca terminaria.
# Consuma so o que precisa, com next ou um for com break:
g = pares(naturais())
print(next(g), next(g), next(g)) # 0 2 4Um gerador infinito com while True; consuma só o necessário, nunca por completo.
islice: cortar o infinito com segurança
A ferramenta certa para pegar só um pedaço de um gerador, inclusive infinito, é o itertools.islice. Ele funciona como o fatiamento de listas, mas de forma preguiçosa: em vez de exigir a sequência inteira, ele consome do gerador apenas os itens que você pediu e para. Assim você combina a expressividade de um gerador infinito com a segurança de nunca tentar percorrê-lo por completo. É a dupla que resolve a maioria dos casos de sequências sem fim.
from itertools import islice
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Pega so os 10 primeiros de uma sequencia infinita
primeiros_dez = list(islice(fibonacci(), 10))
print(primeiros_dez) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# islice tambem aceita inicio, fim e passo, como o fatiamento
print(list(islice(fibonacci(), 5, 10))) # [5, 8, 13, 21, 34]islice consome só os itens pedidos de um gerador infinito e para com segurança.
Repare como fibonacci é um gerador infinito limpo, sem nenhuma preocupação com quantos números serão pedidos, e o islice cuida de cortar. Essa separação de responsabilidades é o coração do estilo preguiçoso: o gerador descreve a sequência, e quem consome decide quanto quer. Junte pipelines, geradores infinitos e islice, e você tem um conjunto de ferramentas capaz de processar fluxos de dados enormes ou até ilimitados com uso de memória mínimo e código legível.
Teste rápido
Como pegar com segurança os primeiros 10 valores de um gerador infinito?
Perguntas frequentes
- Por que o pipeline não gasta memória com dados grandes?
- Porque cada etapa é um gerador preguiçoso: nenhum passo constrói uma lista completa. Quando o consumidor final pede um valor, esse pedido se propaga etapa por etapa até a fonte, e um único item viaja por todo o pipeline de cada vez. Nunca existe uma coleção inteira de resultados intermediários, então a memória fica quase constante independentemente do tamanho da entrada.
- Um gerador infinito é perigoso?
- Ele é seguro enquanto ninguém tenta percorrê-lo por completo. O perigo é chamar list, sum, sorted ou um for sem saída sobre ele, o que roda para sempre. A regra é sempre limitar o consumo: use next algumas vezes, um for com break, ou o itertools.islice. A condição de parada mora em quem consome, não no gerador.
- O islice é diferente do fatiamento com colchetes?
- É. O fatiamento com colchetes, como lista[5:10], exige uma sequência já materializada e cria uma nova. O islice funciona sobre qualquer iterável, inclusive geradores e sequências infinitas, consumindo os itens sob demanda sem exigir a coleção inteira. Por isso o islice é a ferramenta para fatiar geradores, onde o colchete não se aplica.
- Posso usar generator expressions no lugar das funções do pipeline?
- Pode, e é comum. Cada etapa do pipeline pode ser uma generator expression em vez de uma função com yield, por exemplo (int(l) for l in linhas). Para etapas simples fica mais enxuto; para lógicas maiores, a função nomeada com yield é mais legível. Os dois se encadeiam da mesma forma e mantêm o comportamento preguiçoso.
- O módulo itertools tem outras ferramentas úteis para geradores?
- Tem muitas. Além do islice, há o chain para encadear iteráveis, o takewhile e o dropwhile para cortar por condição, o count e o cycle para sequências infinitas prontas, entre outros. Todas seguem o mesmo espírito preguiçoso e se combinam com seus geradores. Vale conhecer o itertools ao trabalhar bastante com fluxos de dados.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.