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.

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 4

Um 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.