itertools.batched(): Dividindo Iteráveis em Lotes

Como a função batched() do módulo itertools do Python 3.12 divide qualquer iterável em tuplas de tamanho fixo de forma preguiçosa, eliminando implementações manuais de chunking com islice e laços while.

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

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.

documentação oficial

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.

Tecnologia:
python

Deixe seu Comentário!

Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.

Comentário
Nome
Email
lock Seu e-mail não será publicado.