Iteradores e geradores : como processar dados consumindo menos memória

Iteradores e geradores : como processar dados consumindo menos memória
3 min de leitura

Um script responsável por resumir o diário de pagamentos funcionou sem incidentes durante um ano, antes de falhar num ficheiro demasiado grande. Um tutorial dedicado aos iteradores e aos geradores de Python mostra como construir cadeias de processamento que leem os dados um registo de cada vez, sem os carregar todos para a memória.

  • Um script de pagamentos falhou porque carregava todos os registos numa lista.
  • Os iteradores e geradores leem os dados um registo de cada vez, gastando pouca memória.
  • Os geradores não são mais rápidos, mas limitam a memória, e operações como a ordenação global continuam a exigir todos os registos.
  • Num teste de uma única execução, a versão em lista atingiu 198 MB, contra 0,4 MB na versão em fluxo.

No caso descrito, o script abria o ficheiro, construía uma lista de registos analisados, filtrava os erros e depois exibia uma tabela. Após uma alteração na rotação dos diários, uma semana de tráfego ficou num único ficheiro de 9 GB. O processamento noturno interrompeu-se com um MemoryError antes de produzir qualquer resumo. Segundo o autor, a lógica de análise não estava em causa: o problema vinha da lista, que obriga a manter todos os registos ao mesmo tempo.

O funcionamento baseia-se num contrato simples. Um iterável, como uma lista, uma cadeia de caracteres ou um ficheiro aberto, pode fornecer um iterador, ou seja, um cursor que avança elemento a elemento e nunca retrocede. O ciclo for chama estas etapas automaticamente, e o fim da sequência é sinalizado pela exceção StopIteration, que não é um erro. Um iterador esgotado não se reinicializa: uma função que devolve « nada » numa segunda leitura não está necessariamente vazia, simplesmente já foi consumida.

O tutorial recomenda começar pelo objeto ficheiro, que já é um iterador. Ler um ficheiro com f.readlines() cria uma lista com todas as linhas, enquanto que for line in f lê apenas um bloco tamponado de cada vez. As funções geradoras, que usam a palavra-chave yield, permitem depois dividir a análise em etapas nomeadas. A instrução yield from encadeia vários ficheiros de rotação como um único fluxo, e as expressões geradoras, escritas entre parênteses, servem para pequenas transformações intermédias. O autor alerta para uma armadilha: a parte mais à esquerda de uma expressão geradora é avaliada imediatamente.

Medir antes de confiar

O tutorial também insiste nos pontos onde a lista regressa discretamente. As chamadas list(), sorted(), um dicionário que agrupa os registos por identificador de pedido, ou a construção de um DataFrame reintroduzem a totalidade do fluxo na memória. Os geradores também não são mais rápidos do que as listas: o seu interesse é uma memória limitada e etapas reorganizáveis. Para os processamentos em lote, o Python 3.12 propõe itertools.batched(), e é dada uma receita equivalente em islice para as versões anteriores.

Para verificar o comportamento real, o autor recomenda o módulo tracemalloc, que mede a memória alocada pelo Python, em valor corrente e em pico. Num diário sintético de 200 000 linhas, com cerca de 17 MB, a versão « lista » atinge um pico de 198 MB num computador portátil, contra 0,4 MB para a versão em fluxo. O autor precisa que se trata dos resultados de uma única execução, e não de um benchmark. Recorda também que o tracemalloc não vê as alocações das extensões em C nem a cache do sistema.

Os limites do fluxo

Alguns processamentos não podem ser conduzidos em fluxo. Uma ordenação global, uma desduplicação exata sobre todo o ficheiro ou um percentil calculado sobre todos os valores de latência exigem o conhecimento de todos os registos. Nesse caso, o tutorial menciona um algoritmo aproximado baseado numa amostra, ou uma ferramenta externa que ordena em disco.

O tutorial precisa ainda que esta abordagem permanece em Python puro. Pandas, Spark, Dask ou os fluxos assíncronos respondem a problemas semelhantes, mas não são necessários para um ficheiro de diário, uma exportação ou um despejo de dados que cabe no disco sem caber na memória RAM.

S’abonner
Notification pour
guest

0 Commentaires
Le plus ancien
Le plus récent Le plus populaire
0
Nous aimerions avoir votre avis, veuillez laisser un commentaire.x