Ao processar muitos itens, é comum dividir uma sequência em grupos menores. O Python oferece itertools.batched() para fazer isso sem criar lógica manual, e versões recentes também permitem usar o parâmetro strict. Neste guia, você vai entender como a função trabalha, quando o modo estrito ajuda e como evitar erros em tarefas de dados, arquivos, APIs e automações.
O que é itertools.batched
itertools.batched(iterable, n) recebe qualquer iterável e devolve tuplas com até n elementos. Como o resultado é produzido sob demanda, a função combina bem com arquivos grandes, geradores e pipelines que não devem carregar tudo na memória.
from itertools import batched
numeros = range(10)
for lote in batched(numeros, 3):
print(lote)
A saída contém grupos de três elementos, exceto o último, que pode ser menor. Esse comportamento é prático quando você quer processar tudo, mesmo que o tamanho total não seja múltiplo do lote.
O que muda com strict=True
Com strict=True, o último lote também precisa conter exatamente n itens. Se faltarem elementos, a função gera ValueError. Isso transforma uma suposição silenciosa em uma validação explícita.
from itertools import batched
for lote in batched(range(9), 3, strict=True):
print(lote)
Esse exemplo funciona porque nove é divisível por três. Se o intervalo terminasse em dez, o último lote teria um item e o erro seria disparado. O modo estrito é útil quando cada grupo representa uma estrutura fixa: coordenadas, registros pareados, blocos criptográficos, linhas de importação ou argumentos de uma operação.
Quando usar o modo padrão
Use o comportamento padrão quando um lote incompleto ainda é válido. Um envio de mensagens pode aceitar o último grupo com menos destinatários. Um script de redimensionamento de imagens pode trabalhar com cinco arquivos por vez e terminar com apenas dois. Um consumidor de API pode enviar até cem itens por requisição, sem exigir exatamente cem.
Nesses casos, rejeitar o último lote só criaria complexidade. O objetivo é limitar o tamanho máximo, não impor uma cardinalidade fixa.
Quando strict=True é melhor
O modo estrito é indicado quando o formato dos dados exige grupos completos. Imagine uma lista plana de coordenadas: latitude, longitude, altitude. Cada registro precisa ter três valores. Se um valor estiver ausente, aceitar uma tupla menor esconderia um problema de origem.
valores = [10.2, -48.1, 800, 11.0, -47.9, 820]
coordenadas = list(batched(valores, 3, strict=True))
O mesmo raciocínio vale para pares chave-valor, RGB, matrizes, lotes de treinamento com formato rígido e dados vindos de sensores.
Validação antes do processamento
Você pode verificar o tamanho antes quando a coleção oferece len(). Porém, iteradores e geradores muitas vezes não possuem tamanho conhecido. O valor de strict é justamente validar durante o consumo.
def processar_registros(origem):
try:
for lote in batched(origem, 4, strict=True):
salvar(lote)
except ValueError as erro:
registrar_erro(str(erro))
raise
Esse padrão evita salvar parcialmente uma estrutura inválida. Em sistemas críticos, considere também transações, arquivos temporários ou filas de compensação.
Uso com arquivos grandes
Arquivos podem ser processados linha por linha. Assim, o consumo de memória permanece baixo.
from itertools import batched
with open('dados.txt', encoding='utf-8') as arquivo:
for linhas in batched(arquivo, 1000):
normalizadas = [linha.strip() for linha in linhas]
enviar(normalizadas)
Esse exemplo aceita um último lote menor. Para um formato em que cada registro ocupa exatamente quatro linhas, use strict=True. Se o arquivo terminar no meio de um registro, o erro sinaliza corrupção ou exportação incompleta.
Uso com APIs
Muitas APIs limitam quantos IDs podem ser enviados por chamada. batched() simplifica esse controle.
for ids in batched(lista_de_ids, 50):
resposta = cliente.buscar(ids=list(ids))
armazenar(resposta)
Convertemos a tupla em lista apenas quando a biblioteca cliente exige JSON serializável nesse formato. Não use strict=True aqui, a menos que o contrato da API realmente exija cinquenta IDs em cada chamada.
Uso em bancos de dados
Inserções em lote reduzem viagens ao banco. Ainda assim, lotes enormes podem aumentar bloqueios e consumo de memória.
for registros in batched(gerar_registros(), 500):
cursor.executemany(sql, registros)
conexao.commit()
Escolha o tamanho medindo o ambiente real. Para aprender mais, veja também o conteúdo sobre Python e SQLite e o guia de Python com MySQL.
Diferença para fatiamento de listas
Uma alternativa tradicional é usar índices e slices. Ela funciona bem para listas pequenas, mas depende de uma sequência indexável e normalmente já carregada na memória. batched() aceita qualquer iterável e preserva a avaliação preguiçosa.
lotes = [dados[i:i + 100] for i in range(0, len(dados), 100)]
Para geradores, streams ou leitura incremental, a solução com itertools costuma ser mais adequada. O módulo é apresentado em Introdução ao itertools.
Cuidados com geradores
Um gerador é consumido uma única vez. Depois que batched() avança, os itens anteriores não podem ser recuperados. Evite tentar contar e depois reutilizar o mesmo gerador. Se a validação falhar no último lote, os lotes anteriores já podem ter sido processados.
Quando atomicidade for importante, acumule em armazenamento temporário, valide previamente quando possível ou use transações. Em pipelines simples, apenas registrar o erro e interromper pode ser suficiente.
Tratando o ValueError
Não capture o erro apenas para ignorá-lo. O propósito do modo estrito é denunciar um lote incompleto. Registre contexto suficiente para encontrar a causa.
try:
grupos = list(batched(valores, 3, strict=True))
except ValueError as exc:
raise ValueError('Dados devem formar grupos de três') from exc
Uma mensagem de domínio é mais útil do que expor apenas o detalhe interno. Para boas práticas gerais, consulte Try Except no Python.
Compatibilidade de versão
Antes de usar o parâmetro, confirme a versão do Python do servidor, da aplicação e do pipeline de CI. Ambientes locais e de produção podem estar diferentes. Consulte a documentação oficial de itertools e as notas de versão do Python para verificar quando cada recurso está disponível.
Testes recomendados
Crie testes para entrada vazia, tamanho exato, tamanho menor que o lote, múltiplos lotes e último lote incompleto. Verifique também iteradores infinitos com cuidado: batched() continuará produzindo grupos enquanto houver consumo.
def test_lotes_exatos():
assert list(batched(range(6), 3, strict=True)) == [(0, 1, 2), (3, 4, 5)]
O conteúdo sobre testes unitários em Python ajuda a estruturar esses cenários.
Boas práticas
Dê nomes que expressem o significado do lote, não apenas “chunk”. Documente por que o tamanho foi escolhido. Use constantes quando o valor fizer parte de um contrato. Aplique strict=True somente quando lote incompleto for realmente erro. Evite converter todos os lotes em lista se o objetivo é economizar memória. Monitore tempo, falhas e quantidade de itens processados.
Conclusão
itertools.batched() oferece uma forma clara e eficiente de dividir iteráveis. O modo padrão serve para limites máximos e aceita o restante. strict=True serve para formatos rígidos e impede que dados incompletos passem silenciosamente. Ao escolher entre os dois, pense no contrato do seu domínio: o último grupo menor é válido ou representa corrupção? Essa resposta define a opção correta.







