Dividir uma sequência em pedaços de tamanho fixo é uma necessidade recorrente: processar uma lista em lotes, montar páginas de resultados, agrupar itens antes de enviar para uma API que só aceita um número limitado por vez. Antes do Python 3.12, isso exigia escrever um laço manual com islice ou uma função auxiliar copiada de projeto em projeto. A função itertools.batched() resolve isso de forma nativa: recebe um iterável e um tamanho n, e devolve tuplas com até n itens cada.
O jeito antigo
Sem batched(), o padrão comum era combinar iter() com islice() dentro de um laço while:
from itertools import islice
def em_lotes(iteravel, n):
iterador = iter(iteravel)
while lote := tuple(islice(iterador, n)):
yield lote
for lote in em_lotes(range(10), 3):
print(lote)
# (0, 1, 2)
# (3, 4, 5)
# (6, 7, 8)
# (9,)
Funciona, mas é código que se repete em vários projetos só para resolver algo simples. E é exatamente essa a implementação que o Python 3.12 passou a oferecer pronta, dentro do próprio módulo itertools.
Como funciona o batched()
A assinatura é batched(iteravel, n), e o retorno é um iterador de tuplas. O último lote pode ser menor que n se a quantidade de itens não for múltipla do tamanho do lote:
from itertools import batched
for lote in batched("ABCDEFG", 3):
print(lote)
# ('A', 'B', 'C')
# ('D', 'E', 'F')
# ('G',)
Como qualquer função do itertools, batched() é preguiçosa (lazy): os lotes só são montados conforme o iterador é consumido, então funciona bem mesmo com sequências muito grandes ou infinitas — desde que você não tente materializar tudo de uma vez com list().
Exemplo prático: processando uma lista em blocos
Um caso comum é enviar registros para uma API ou banco de dados em blocos, para não estourar limites de tamanho de requisição:
from itertools import batched
ids = list(range(1, 24))
for lote in batched(ids, 5):
print(f"Buscando registros: {lote}")
# api.buscar_varios(lote)
Sem batched(), esse controle de índices costumava ser feito manualmente com fatiamento (ids[i:i+5]) e um laço for com passo — mais propenso a erro de índice do que simplesmente iterar sobre os lotes prontos.
Exemplo prático: paginação de resultados
batched() também combina bem com outros iteráveis preguiçosos, como generators que leem um arquivo linha a linha:
from itertools import batched
def linhas_do_arquivo(caminho):
with open(caminho, encoding="utf-8") as arquivo:
for linha in arquivo:
yield linha.rstrip("\n")
for pagina in batched(linhas_do_arquivo("log.txt"), 50):
processar_pagina(pagina)
Nenhum lote é montado antes da hora, e o arquivo nunca precisa ser carregado inteiro na memória.
Garantindo lotes uniformes com strict
Desde o Python 3.13, batched() aceita o parâmetro nomeado strict. Com strict=True, uma exceção é levantada se o último lote não tiver exatamente n itens — útil quando um lote incompleto indica um erro nos dados, e não um caso válido a ser tratado silenciosamente:
from itertools import batched
pares = list(batched([1, 2, 3, 4, 5, 6], 2, strict=True))
print(pares) # [(1, 2), (3, 4), (5, 6)]
# Com 7 itens, o último lote ficaria incompleto:
list(batched([1, 2, 3, 4, 5, 6, 7], 2, strict=True))
# ValueError: batched(): incomplete batch
Quando usar
- Enviar dados em lotes para uma API, fila ou banco de dados que impõe um limite de itens por chamada.
- Paginar resultados de uma consulta ou de um arquivo grande sem carregar tudo na memória.
- Dividir uma sequência em grupos de tamanho fixo para processamento paralelo (um lote por worker, por exemplo).
- Substituir implementações manuais de "chunking" espalhadas pelo projeto por uma função padrão da biblioteca.
Cuidados
batched() não agrupa por valor ou por chave — para isso o certo é itertools.groupby(). Ela apenas corta o iterável em pedaços de tamanho fixo, na ordem em que os itens aparecem. Também é importante lembrar que o resultado é um iterador: se precisar acessar os lotes mais de uma vez ou por índice, é necessário convertê-lo para list() explicitamente. E, como a função só existe a partir do Python 3.12, projetos que precisam suportar versões anteriores ainda dependem de uma implementação manual ou de bibliotecas como o more-itertools.
Compatibilidade
itertools.batched() foi introduzida no Python 3.12, lançado em outubro de 2023, e o parâmetro strict foi adicionado no Python 3.13. Não depende de nenhuma biblioteca externa, apenas da versão do interpretador.
Conclusão
O itertools.batched() pega um padrão que praticamente todo projeto Python acaba reimplementando — dividir uma sequência em pedaços de tamanho fixo — e transforma em uma única chamada de biblioteca padrão, preguiçosa e testada. Para quem já usa itertools no dia a dia, é uma função a mais que evita reescrever a mesma lógica de fatiamento pela enésima vez.
0 Comentário
Deixe seu Comentário!
Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.