---
title: "Módulo itertools do Python: O Guia Prático para Iteração Elegante"
url: "https://python.dev.br/blog/python-itertools-iteracao-elegante/"
markdown_url: "https://python.dev.br/blog/python-itertools-iteracao-elegante.MD"
description: "Aprenda a usar o módulo itertools do Python para encadear, fatiar, agrupar e acumular iteradores sem criar listas intermediárias. Guia prático em português brasileiro com chain, islice, groupby, accumulate, product, combinations e receitas de processamento de dados."
date: "2026-07-19"
author: "Equipe Python Brasil"
---

# Módulo itertools do Python: O Guia Prático para Iteração Elegante

Aprenda a usar o módulo itertools do Python para encadear, fatiar, agrupar e acumular iteradores sem criar listas intermediárias. Guia prático em português brasileiro com chain, islice, groupby, accumulate, product, combinations e receitas de processamento de dados.


O `itertools` é o módulo da biblioteca padrão do Python dedicado a **iteração eficiente**. Ele oferece funções pequenas, escritas em C, que criam e combinam iteradores — aqueles objetos que produzem um valor de cada vez sob demanda, sem montar a coleção inteira na memória. Se você já escreveu um loop só para concatenar duas listas grandes, fatiar um gerador infinito ou agrupar vendas por filial, provavelmente estava reinventando o que o `itertools` já entrega pronto, testado e rápido.

Este guia cobre as ferramentas mais úteis do módulo — `chain`, `islice`, `groupby`, `accumulate`, `starmap`, `compress`, os iteradores infinitos e os de combinatória — com exemplos em contexto brasileiro. Ele complementa conteúdos como [Geradores e Iteradores](/blog/geradores-iteradores-python/), o [módulo collections](/blog/collections-python-guia-completo/), [Compreensão de Listas](/blog/compreensao-de-listas-python/) e [Estruturas de Dados em Python](/blog/estruturas-de-dados-python/). A ideia não é decorar a API inteira, mas saber qual função pega cada problema.

## Por que usar itertools: o ganho da avaliação preguiçosa

A vantagem central do `itertools` é a **avaliação preguiçosa** (lazy evaluation). Uma [list comprehension](/blog/compreensao-de-listas-python/) como `[x**2 for x in range(1_000_000)]` aloca um milhão de inteiros na memória antes de você usar o primeiro. Um equivalente com `itertools` produz cada valor só quando alguém o consome:

```python
from itertools import count, islice

# count() é um iterador infinito — nunca termina, nunca estoura a memória
# islice() fatia os primeiros 5 valores, como range(), mas sobre qualquer iterador
for i in islice(count(1), 5):
    print(i)
# 1 2 3 4 5
```

Isso permite três coisas que seriam caras ou impossíveis com listas: processar arquivos e fluxos maiores que a RAM, parar no meio com uma condição e encadear transformações sem custo de cópia. Para um [pipeline de dados](/blog/etl-python-2026/), isso é a diferença entre um script que escala e um que estoura a memória num milhão de linhas.

## chain: concatenar iteradores sem custo extra

`chain` recebe vários iteráveis e devolve um único iterador que percorre todos em sequência, **sem criar uma lista intermediária**. É a forma correta de iterar sobre várias fontes:

```python
from itertools import chain

vendas_sp = ["SP-001", "SP-002"]
vendas_rj = ["RJ-001"]
vendas_mg = ["MG-001", "MG-002", "MG-003"]

# Errado (copia tudo para uma lista nova):
for v in vendas_sp + vendas_rj + vendas_mg:
    ...

# Certo (percorre cada fonte na ordem, sem alocar nada extra):
for v in chain(vendas_sp, vendas_rj, vendas_mg):
    print(v)
# SP-001 SP-002 RJ-001 MG-001 MG-002 MG-003
```

A variante `chain.from_iterable` faz o mesmo quando as fontes já estão numa estrutura — por exemplo, uma lista de listas ou um gerador que devolve geradores:

```python
arquivos_por_filial = [
    ["sp_jan.csv", "sp_fev.csv"],
    ["rj_jan.csv"],
    ["mg_jan.csv", "mg_fev.csv", "mg_mar.csv"],
]

# flatten de uma só vez, sem [[x] for sublist in ... for x in sublist]
for nome in chain.from_iterable(arquivos_por_filial):
    print(nome)
```

Combine `chain.from_iterable` com [`pathlib`](/blog/python-pathlib-manipulacao-caminhos-arquivos/) e você tem um varredor de pastas que processa todos os arquivos de várias filiais em um único `for`.

## islice: fatiar um iterador como uma lista

`islice(iterable, stop)` ou `islice(iterable, start, stop, step)` funciona como o fatiamento de lista `lista[start:stop:step]`, mas sobre **qualquer iterador** — inclusive infinitos ou arquivos grandes:

```python
from itertools import islice

with open("vendas.csv", encoding="utf-8") as f:
    # Pega só as 10 primeiras linhas do arquivo, sem carregar o arquivo inteiro
    for linha in islice(f, 10):
        print(linha.strip())
```

O `islice` é a ferramenta certa quando você quer **os primeiros N** resultados de um gerador caro (uma consulta paginada, uma busca, um modelo de ML) e não pode materializar tudo. Ele não aceita índices negativos como o fatiamento de lista — iteradores só andam para frente.

## groupby: agrupar elementos consecutivos

`groupby(iterable, key)` agrupa elementos consecutivos que compartilham a mesma chave. O detalhe que derruba todo iniciante: ele agrupa apenas **consecutivos**, não todos os iguais. Por isso a receita padrão é ordenar antes:

```python
from itertools import groupby

vendas = [
    ("SP", 1200), ("RJ", 800), ("SP", 300), ("MG", 500),
    ("RJ", 200), ("SP", 700), ("MG", 900),
]

# 1) ordenar pela chave de agrupamento (obrigatório para juntar todos do mesmo estado)
vendas_ordenadas = sorted(vendas, key=lambda v: v[0])

# 2) agrupar
for estado, grupo in groupby(vendas_ordenadas, key=lambda v: v[0]):
    total = sum(valor for _, valor in grupo)
    print(f"{estado}: R$ {total}")
# MG: R$ 1400
# RJ: R$ 1000
# SP: R$ 2200
```

O `groupby` devolve, para cada chave, um iterador temporário que precisa ser consumido antes de avançar para o próximo grupo. Se você guardar o grupo numa lista e continuar, ele estará vazio. Para persistir, materialize: `{k: list(g) for k, g in groupby(...)}`.

Para contagens de frequência sem ordenação, use [`Counter`](/blog/collections-python-guia-completo/) do módulo `collections` — ele é mais direto quando você só quer saber "quantos de cada".

## accumulate: somatório e redução com histórico

`accumulate(iterable, func)` devolve o total acumulado em cada passo — como um [`functools.reduce`](https://docs.python.org/3/library/functools.html) que mantém o histórico de todas as etapas:

```python
from itertools import accumulate
import operator

faturamento = [500, 800, 300, 1000]

# Soma acumulada (comportamento padrão)
list(accumulate(faturamento))
# [500, 1300, 1600, 2600]

# Acumulado com outra operação: o maior valor visto até cada passo
list(accumulate(faturamento, max))
# [500, 800, 800, 1000]

# Produto acumulado
list(accumulate(faturamento, operator.mul))
# [500, 400000, 120000000, 120000000000000]
```

É a base de indicadores de "running total" em [dashboards com Streamlit](/blog/criando-dashboards-com-streamlit/): faturamento acumulado no ano, saldo de conta corrente, meta acumulada de vendas.

## takewhile e dropwhile: parar ou pular por condição

`takewhile(condição, iterável)` devolve elementos **enquanto** a condição for verdadeira e para no primeiro falso. `dropwhile` faz o oposto: descarta enquanto a condição é verdadeira e devolve o restante inteiro:

```python
from itertools import takewhile, dropwhile

logs = ["OK", "OK", "OK", "ERRO", "OK", "ERRO"]

# Pegar até o primeiro ERRO
list(takewhile(lambda x: x == "OK", logs))
# ['OK', 'OK', 'OK']

# Descartar até o primeiro ERRO (depois entrega tudo, mesmo os OK seguintes)
list(dropwhile(lambda x: x == "OK", logs))
# ['ERRO', 'OK', 'ERRO']
```

São ideais para processar arquivos com cabeçalho/rodapé, parar num marcador ou extrair só a faixa válida de uma série temporal.

## starmap e compress: mapear tuplas e filtrar por máscara

`starmap(func, iterável_de_tuplas)` aplica uma função já **desempacotando** os argumentos de cada tupla — equivalente a `func(*args)` em loop:

```python
from itertools import starmap

transacoes = [("compra", 100), ("venda", 200), ("compra", 50)]

def classificar(tipo, valor):
    return f"{tipo.upper()} de R${valor}"

list(starmap(classificar, transacoes))
# ['COMPRA de R$100', 'VENDA de R$200', 'COMPRA de R$50']
```

`compress(dados, seletores)` filtra `dados` por uma sequência paralela de booleanos (uma máscara):

```python
from itertools import compress

emails = ["a@x.com", "b@x.com", "c@x.com"]
validos = [True, False, True]

list(compress(emails, validos))
# ['a@x.com', 'c@x.com']
```

É a forma mais limpa de aplicar o resultado de uma validação vetorizada sem cair num `if` dentro de [comprehension](/blog/compreensao-de-listas-python/).

## Iteradores infinitos: count, cycle e repeat

Três geradores que nunca param sozinhos:

```python
from itertools import count, cycle, repeat

# count(start, step): 0, 1, 2, 3, ... para sempre
for i in count(10, 2):
    if i > 16:
        break
    # 10, 12, 14, 16

# cycle(iter): repete a sequência indefinidamente
turnos = cycle(["manhã", "tarde", "noite"])
for i, t in zip(range(7), turnos):
    print(f"dia {i}: {t}")

# repeat(obj, n): devolve o mesmo objeto n vezes (ou para sempre se n for omitido)
list(repeat("placeholder", 3))
# ['placeholder', 'placeholder', 'placeholder']
```

Sempre combine iteradores infinitos com `islice`, `takewhile` ou `zip` para garantir o término — um `for` puro sobre `count()` roda para sempre.

## Combinatória: product, combinations e permutations

A família de combinatória resolve problemas de "todas as possibilidades" sem recursão manual:

```python
from itertools import product, combinations, permutations

# product: produto cartesiano (todas as combinações entre listas)
cores = ["verde", "azul"]
tamanhos = ["P", "M"]
list(product(cores, tamanhos))
# [('verde','P'), ('verde','M'), ('azul','P'), ('azul','M')]

# combinations: subconjuntos de tamanho r, sem ordem, sem repetição
vendedores = ["Ana", "Beto", "Caio", "Duda"]
list(combinations(vendedores, 2))   # duplas para um projeto
# [('Ana','Beto'), ('Ana','Caio'), ('Ana','Duda'), ('Beto','Caio'), ('Beto','Duda'), ('Caio','Duda')]

# permutations: sequências em que a ordem importa
list(permutations("AB", 2))
# [('A','B'), ('B','A')]
```

Atenção ao tamanho: `permutations` e `product` explodem combinatorialmente. `permutations(range(10), 10)` gera mais de 3,6 milhões de resultados — confirme que você realmente precisa deles antes de pedir.

## Receita brasileira: paginar e agrupar vendas

Um caso de uso que junta várias funções: você tem um arquivo grande de vendas e quer (1) mostrar as 5 primeiras linhas como amostra e (2) totalizar por estado. Com `itertools`, nenhum dado é duplicado na memória:

```python
from itertools import islice, groupby

def carregar_vendas(caminho):
    """Devolve uma lista de (estado, valor) a partir de um CSV estado,valor."""
    linhas = []
    with open(caminho, encoding="utf-8") as f:
        for linha in f:
            estado, valor = linha.strip().split(",")
            linhas.append((estado, int(valor)))
    return linhas

vendas = carregar_vendas("vendas.csv")

# 1) Amostra: as 5 primeiras vendas (sem carregar arquivo inteiro se for um gerador)
print("Amostra:")
for estado, valor in islice(vendas, 5):
    print(f"  {estado}: R$ {valor}")

# 2) Total por estado (ordena antes de agrupar!)
vendas_ordenadas = sorted(vendas, key=lambda v: v[0])
print("Totais por estado:")
for estado, grupo in groupby(vendas_ordenadas, key=lambda v: v[0]):
    print(f"  {estado}: R$ {sum(v for _, v in grupo)}")
```

O mesmo molde serve para consolidar [notas fiscais](/blog/python-csv-leitura-escrita-arquivos/) por mês, agregar [métricas de API](/blog/apis-rest-com-fastapi/) por rota ou agrupar [linhas de log](/blog/logging-em-python/) por nível.

## itertools vs loops manuais: quando vale a pena

O `itertools` não substitui todo loop. A regra prática é usar quando você quer **lazy evaluation**, **encadeamento de transformações** ou **operações que o módulo já tem nome** (`groupby`, `accumulate`, `combinations`). Quando a lógica é simples e a lista é pequena, uma list comprehension costuma ser mais legível para quem lê o código depois:

| Objetivo | Loop/comprehension | itertools |
| --- | --- | --- |
| Concatenar listas | `a + b + c` | `chain(a, b, c)` |
| Achatar lista de listas | `[x for sub in L for x in sub]` | `chain.from_iterable(L)` |
| Primeiros N de um gerador | `list(g)[:N]` (materializa tudo!) | `islice(g, N)` |
| Agrupar por chave | manual com `dict` | `groupby` (após `sorted`) |
| Soma acumulada | loop com `total += x` | `accumulate` |
| Todas as duplas | dois `for` aninhados | `combinations(L, 2)` |

Prefira `itertools` quando a origem é um iterador/gerador, o volume é grande ou a operação aparece com frequência e tem nome próprio.

## Boas práticas

- **Sempre pare iteradores infinitos.** Combine `count`, `cycle` e `repeat` com `islice`, `takewhile` ou `zip` para evitar loops eternos.
- **Ordene antes de `groupby`.** O `groupby` agrupa só consecutivos; sem ordenação prévia pela chave, você terá grupos duplicados.
- **Consuma o grupo na hora.** O iterador de cada grupo de `groupby` é descartado ao avançar; materialize com `list(g)` se precisar guardar.
- **Prefira iteradores a listas em pipelines grandes.** Encadear `islice` → `takewhile` → `map` mantém a memória constante, mesmo com milhões de itens.
- **Cuidado com combinatória.** `permutations` e `product` crescem fatorialmente; valide o tamanho da entrada antes de pedir todas as combinações.
- **Importe só o que usa.** `from itertools import chain, groupby` é mais legível que `itertools.chain` espalhado pelo código.

## Conclusão

O `itertools` é a ferramenta que separa o Python "que funciona" do Python "que escala". Suas funções pequenas, escritas em C, resolvem os padrões mais comuns de iteração — concatenar, fatiar, agrupar, acumular, combinar — mantendo a memória constante e o código curto. Para qualquer processamento de dados que envolva volumes grandes, fluxos infinitos ou agrupamento por regra, comece perguntando "isso já tem no `itertools`?".

Para continuar a sequência de fundamentos da biblioteca padrão, veja como combinar `itertools` com [`collections`](/blog/collections-python-guia-completo/) para agregar e contar, com [geradores e o protocolo de iteração](/blog/geradores-iteradores-python/) para entender o que acontece por baixo, e com [expressões regulares](/blog/expressoes-regulares-python/) para filtrar as linhas antes de agrupá-las.

## Perguntas frequentes

### O itertools vem instalado com o Python ou preciso instalar algo?

O `itertools` faz parte da biblioteca padrão do Python desde a versão 2.3 e não precisa de instalação com `pip` — basta fazer `from itertools import chain, groupby`. Ele é escrito em C, então é mais rápido do que reimplementar a mesma lógica com loops e listas em Python puro.

### Qual a diferença entre itertools e uma list comprehension?

A list comprehension cria a lista inteira na memória antes de você usá-la; o `itertools` devolve um iterador que produz os elementos sob demanda (lazy evaluation). Para volumes grandes, o `itertools` consome pouca memória e permite parar no meio com `islice` ou `takewhile`. Para listas pequenas, a list comprehension costuma ser mais legível.

### Por que meu groupby não agrupou todos os itens iguais?

O `groupby` do Python só agrupa elementos **consecutivos** iguais, e não todos os iguais do iterador. Se os dados não estiverem ordenados pela chave de agrupamento, você terá vários grupos repetidos. A receita correta é ordenar antes: `dados_ordenados = sorted(dados, key=chave)` e então `itertools.groupby(dados_ordenados, key=chave)`.

### Como acumular uma soma parcial (running total) sem escrever um loop?

Use `itertools.accumulate(valores)`. Ele devolve um iterador com o total acumulado em cada passo: `list(accumulate([100, 200, 50]))` retorna `[100, 300, 350]`. Você pode passar uma função diferente, como `min`, `max` ou `operator.mul`, para acumular de outra forma.

### Qual a diferença entre product, combinations e permutations?

`product(A, B)` calcula o produto cartesiano (todos os pares possíveis entre A e B, com repetição entre as listas). `combinations(iteravel, r)` gera subconjuntos de tamanho `r` sem repetição e sem ordem (o par `(a,b)` é igual a `(b,a)` e aparece uma vez). `permutations(iteravel, r)` gera sequências de tamanho `r` onde a ordem importa e os elementos não se repetem dentro de cada grupo.
