Módulo 13 - Geradores e iteradores

Prática: processar um arquivo grande

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

O que você vai aprender

  • Ler um arquivo linha a linha com um gerador, sem carregar tudo.
  • Encadear um filtro sobre as linhas do arquivo.
  • Contar ocorrências percorrendo o fluxo uma única vez.
  • Reconhecer que o objeto arquivo já é um iterador de linhas.

Ler o arquivo sem engolir tudo

Este é o cenário que motiva tudo o que você viu no módulo. Imagine um arquivo de log com milhões de linhas, grande demais para caber confortavelmente na memória. Você quer contar quantas linhas contêm a palavra ERRO. A abordagem ingênua seria ler o arquivo inteiro para uma lista com readlines() e depois filtrar. Em um arquivo enorme, isso pode esgotar a memória antes mesmo de começar a contar. A abordagem com geradores lê e processa uma linha por vez, gastando a memória de uma linha só, não importa se o arquivo tem mil ou dez milhões de linhas.

O detalhe que torna isso natural no Python é que o próprio objeto arquivo já é um iterador de linhas. Quando você escreve for linha in arquivo, o Python entrega uma linha de cada vez, lendo do disco sob demanda, exatamente como um gerador. Vamos aproveitar isso e envolver a leitura em um gerador nosso, que também remove o espaço em branco das pontas de cada linha. Assim separamos a responsabilidade de ler da responsabilidade de contar, no espírito de pipeline da aula anterior.

# Primeiro, criamos um arquivo de exemplo para a pratica.
with open("log.txt", "w", encoding="utf-8") as f:
    f.write("INFO servidor iniciado\n")
    f.write("ERRO falha ao conectar\n")
    f.write("INFO requisicao recebida\n")
    f.write("ERRO tempo esgotado\n")
    f.write("INFO tudo certo\n")

def ler_linhas(caminho):
    with open(caminho, encoding="utf-8") as arquivo:
        for linha in arquivo:      # uma linha por vez, sob demanda
            yield linha.strip()    # remove espaco e quebra de linha

for linha in ler_linhas("log.txt"):
    print(linha)

# INFO servidor iniciado
# ERRO falha ao conectar
# INFO requisicao recebida
# ERRO tempo esgotado
# INFO tudo certo

O gerador ler_linhas entrega uma linha limpa por vez, sem carregar o arquivo inteiro.

Filtrar as linhas e contar ocorrências

Com o gerador de leitura pronto, encadeamos um filtro que deixa passar só as linhas com a palavra ERRO, no mesmo estilo de pipeline da aula anterior. O filtro é outro gerador: recebe o fluxo de linhas e devolve apenas as que interessam. Por fim, contamos. Como só precisamos do total, não guardamos as linhas: percorremos o fluxo somando um a cada ocorrência. A função sum combinada com uma generator expression faz isso em uma linha, tratando cada True como 1, mas aqui usamos um laço explícito para deixar a contagem visível.

def apenas_com(linhas, termo):
    for linha in linhas:
        if termo in linha:
            yield linha

# Pipeline: ler o arquivo, filtrar as linhas de ERRO.
linhas = ler_linhas("log.txt")
erros = apenas_com(linhas, "ERRO")

total = 0
for _ in erros:
    total = total + 1

print("Linhas com ERRO:", total)   # Linhas com ERRO: 2

Encadeia leitura e filtro, depois conta as ocorrências percorrendo o fluxo uma única vez.

O mesmo resultado sai de forma ainda mais enxuta passando uma generator expression direto para sum, como na aula de generator expressions. A linha total = sum(1 for linha in ler_linhas("log.txt") if "ERRO" in linha) faz leitura, filtro e contagem de uma vez, sem nenhuma lista intermediária e com memória de uma linha por vez. Escolha a forma pela clareza: o pipeline com etapas nomeadas é mais fácil de ampliar; a expressão única é mais direta para uma contagem simples. As duas compartilham a mesma virtude central, que é nunca carregar o arquivo inteiro na memória.

O que você construiu

Você fechou o módulo com o exemplo que resume o valor dos geradores. Um gerador leu um arquivo linha a linha, sem carregá-lo inteiro; outro gerador filtrou o fluxo; e um contador percorreu tudo uma vez, gastando memória constante. Trocando o pequeno log de exemplo por um arquivo de milhões de linhas, o código não muda em nada e continua leve, porque a memória usada é a de uma linha por vez. Esse é o padrão que profissionais aplicam para processar logs, planilhas gigantes e exportações de bancos de dados sem estourar a máquina.

Teste rápido

Por que ler um arquivo grande com for linha in arquivo economiza memória frente a readlines()?

Perguntas frequentes

Por que não usar arquivo.read() ou readlines() em arquivos grandes?
Porque os dois trazem o conteúdo inteiro para a memória de uma vez: read() como um texto único e readlines() como uma lista de linhas. Em um arquivo de milhões de linhas, isso pode esgotar a memória. Percorrer o arquivo com for linha in arquivo lê uma linha por vez, sob demanda, com memória constante.
O objeto arquivo já é um gerador?
Ele é um iterador de linhas, que na prática se comporta como um gerador para a leitura: entrega uma linha por vez quando percorrido com for. Por isso dá para envolvê-lo em um gerador seu, com yield, para limpar ou filtrar as linhas, encaixando-o num pipeline como qualquer outra fonte iterável.
Por que usar with open(...) ao ler o arquivo?
O with garante que o arquivo seja fechado ao terminar, mesmo se ocorrer um erro no meio. Colocá-lo dentro do gerador de leitura mantém o arquivo aberto enquanto as linhas são consumidas e o fecha quando a iteração acaba. É a forma segura e recomendada de lidar com arquivos no Python.
Como conto as ocorrências sem guardar as linhas?
Percorra o fluxo somando um a cada linha que cumpre a condição, mantendo só um contador. A forma mais enxuta é sum(1 for linha in fonte if condicao), que trata cada acerto como 1 e soma, sem nunca montar uma lista. Assim a contagem funciona igual em arquivos pequenos e gigantes.
Posso contar duas coisas diferentes no mesmo passe?
Pode, usando dois contadores dentro de um único for sobre o gerador de leitura. Isso evita reabrir o arquivo. O que você não pode é reaproveitar o mesmo gerador já esgotado: se precisar de um segundo passe separado, chame o gerador de leitura de novo para abrir o arquivo outra vez.
Esse padrão serve para arquivos que não são de texto simples?
A ideia de processar sob demanda vale para muitos formatos, mas cada um tem sua ferramenta. Para CSV, o módulo csv lê linha a linha; para JSON por linha, você trata cada linha como um registro. O princípio é o mesmo: consumir um registro por vez com um gerador, sem carregar o arquivo inteiro.

Fontes

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