Explorando o Módulo Itertools em Python: Uma Abordagem Detalhada
Conforme a documentação oficial do Python, o módulo itertools
é um recurso poderoso, que disponibiliza um conjunto de ferramentas otimizadas em termos de velocidade e uso de memória para manipulação de iteradores. Estas ferramentas, flexíveis e versáteis, podem ser usadas individualmente ou combinadas para criar e gerenciar iteradores de forma concisa e eficiente, principalmente quando lidamos com grandes volumes de dados.
O itertools
oferece funcionalidades que simplificam o trabalho com iteradores, facilitando a construção de iteradores complexos a partir de iteradores mais simples. O uso deste módulo contribui para a redução de erros e para um código mais limpo, legível e de fácil manutenção.
As funcionalidades do módulo itertools
podem ser categorizadas em três grupos principais, com base no comportamento dos iteradores que elas produzem:
Iteradores Infinitos
Estes iteradores são projetados para lidar com sequências potencialmente infinitas. Eles podem criar loops que se estenderão indefinidamente, a menos que uma condição de saída seja definida. São particularmente úteis para simular ciclos contínuos ou gerar sequências ilimitadas. O módulo itertools
fornece três iteradores infinitos: count()
, cycle()
e repeat()
.
Iteradores Combinatórios
Este grupo engloba funções que facilitam a geração de produtos cartesianos e a execução de combinações e permutações sobre os elementos de um iterável. Estas ferramentas são fundamentais para explorar todas as possibilidades de organizar ou combinar elementos. O itertools
disponibiliza quatro iteradores combinatórios: product()
, permutations()
, combinations()
e combinations_with_replacement()
.
Iteradores de Terminação Baseados na Sequência de Entrada Mais Curta
Estes iteradores operam sobre sequências finitas e produzem uma saída específica, dependendo da função utilizada. Este grupo inclui: accumulate()
, chain()
, chain.from_iterable()
, compress()
, dropwhile()
, filterfalse()
, groupby()
, islice()
, pairwise()
, starmap()
, takewhile()
, tee()
e zip_longest()
.
Vamos agora explorar o funcionamento individual de cada função, agrupadas por tipo:
Iteradores Infinitos
Os três iteradores infinitos do módulo itertools
são:
count()
A função count(start, step)
gera uma progressão numérica infinita, iniciando com o valor start
e incrementando-o a cada passo com o valor step
. Ambos os argumentos são opcionais. Se start
não for fornecido, o valor inicial será 0. Se step
não for especificado, o incremento padrão será 1.
import itertools
# Contagem a partir de 4, com incremento de 2
for i in itertools.count(4, 2):
# Condição para encerrar o loop, evitando iteração infinita
if i == 14:
break
else:
print(i) # Saída: 4, 6, 8, 10, 12
Saída:
4
6
8
10
12
cycle()
A função cycle(iterable)
recebe um iterável e percorre seus elementos de forma cíclica. Após percorrer todos os itens, o ciclo reinicia, permitindo o acesso contínuo aos elementos na ordem original.
Ao utilizar cycle()
, o resultado é armazenado em uma variável para criar um iterador que mantém seu estado. Isso garante que o ciclo não se reinicie a cada iteração, permitindo o acesso sequencial a cada elemento.
import itertools
cores = ["vermelho", "verde", "amarelo"]
# Passando cores para cycle()
ciclo_cores = itertools.cycle(cores)
print(ciclo_cores)
# Intervalo para interromper o loop infinito após 7 impressões
# next() para obter o próximo item do iterador
for i in range(7):
print(next(ciclo_cores))
Saída:
vermelho
verde
amarelo
vermelho
verde
amarelo
vermelho
repeat()
A função repeat(elem, n)
aceita um elemento (elem
) e o repete n
vezes. Se n
não for especificado, o elemento será repetido infinitamente. O elemento pode ser um valor único ou um iterável.
import itertools
for i in itertools.repeat(10, 3):
print(i)
Saída:
10
10
10
Iteradores Combinatórios
Os iteradores combinatórios incluem:
product()
A função product()
calcula o produto cartesiano dos iteráveis fornecidos. Dados dois iteráveis, por exemplo, x = {7, 8} e y = {1, 2, 3}, o produto cartesiano de x e y conterá todas as combinações possíveis de elementos de x e y, onde o primeiro elemento vem de x e o segundo vem de y. O produto cartesiano de x e y, neste caso, seria [(7, 1), (7, 2), (7, 3), (8, 1), (8, 2), (8, 3)].
product()
aceita um parâmetro opcional chamado repeat
, usado para calcular o produto cartesiano de um iterável consigo mesmo. O parâmetro repeat
especifica quantas repetições serão consideradas para cada elemento durante o cálculo do produto cartesiano.
Por exemplo, product('ABCD', repeat=2)
produzirá combinações como (‘A’, ‘A’), (‘A’, ‘B’), (‘A’, ‘C’), e assim por diante. Se repeat
fosse definido como 3, a função geraria combinações como (‘A’, ‘A’, ‘A’), (‘A’, ‘A’, ‘B’), (‘A’, ‘A’, ‘C’), (‘A’, ‘A’, ‘D’) etc.
from itertools import product
# product() com o argumento opcional repeat
print("product() com o argumento opcional repeat")
print(list(product('ABC', repeat = 2)))
# product sem repeat
print("product() SEM um argumento opcional repeat")
print(list(product([7,8], [1,2,3])))
Saída:
product() com o argumento opcional repeat
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]
product() SEM um argumento opcional repeat
[(7, 1), (7, 2), (7, 3), (8, 1), (8, 2), (8, 3)]
permutations()
permutations(iterable, group_size)
retorna todas as permutações possíveis do iterável fornecido. Uma permutação representa o número de maneiras pelas quais os elementos de um conjunto podem ser ordenados. permutations()
recebe um argumento opcional group_size
. Se group_size
não for especificado, as permutações geradas terão o mesmo tamanho que o comprimento do iterável passado para a função.
import itertools
numeros = [1, 2, 3]
permutacoes_com_tamanho = list(itertools.permutations(numeros,2))
permutacoes_sem_tamanho = list(itertools.permutations(numeros))
print("Permutações com tamanho 2")
print(permutacoes_com_tamanho)
print("Permutações SEM argumento de tamanho")
print(permutacoes_sem_tamanho)
Saída:
Permutações com tamanho 2
[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
Permutações SEM argumento de tamanho
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
combinations()
combinations(iterable, size)
retorna todas as combinações possíveis de um iterável, com um determinado comprimento, dos elementos no iterável. O argumento size
especifica o tamanho de cada combinação.
Os resultados são ordenados. As combinações diferem ligeiramente das permutações. Em uma permutação, a ordem importa, mas em uma combinação a ordem não importa. Por exemplo, em [A, B, C] existem 6 permutações: AB, AC, BA, BC, CA, CB, mas apenas 3 combinações: AB, AC, BC.
import itertools
numeros = [1, 2, 3,4]
combinacao_tamanho2 = list(itertools.combinations(numeros,2))
combinacao_tamanho3 = list(itertools.combinations(numeros, 3))
print("Combinações com tamanho 2")
print(combinacao_tamanho2)
print("Combinações com tamanho 3")
print(combinacao_tamanho3)
Saída:
Combinações com tamanho 2
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
Combinações com tamanho 3
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
combinations_with_replacement()
combinations_with_replacement(iterable, size)
gera todas as combinações possíveis de um iterável de um determinado comprimento, permitindo a repetição de elementos nas combinações de saída. O argumento size
define o tamanho das combinações geradas.
Esta função difere de combinations()
porque fornece combinações em que um elemento pode ser repetido mais de uma vez. Por exemplo, é possível obter uma combinação como (1,1), o que não seria possível usando combinations()
.
import itertools
numeros = [1, 2, 3,4]
combinacao_tamanho2 = list(itertools.combinations_with_replacement(numeros,2))
print("Combinations_with_replacement => tamanho 2")
print(combinacao_tamanho2)
Saída
Combinations_with_replacement => tamanho 2
[(1, 1), (1, 2), (1, 3), (1, 4), (2, 2), (2, 3), (2, 4), (3, 3), (3, 4), (4, 4)]
Iteradores de Terminação
Estes incluem iteradores como:
accumulate()
accumulate(iterable, function)
aceita um iterável e uma função (opcional). Em seguida, retorna o resultado acumulado da aplicação da função a cada iteração nos elementos do iterável. Se nenhuma função for fornecida, a operação de soma é realizada e os resultados acumulados são retornados.
import itertools
import operator
numeros = [1, 2, 3, 4, 5]
# Acumula a soma dos números
acumulado_soma = itertools.accumulate(numeros)
acumulado_multiplicacao = itertools.accumulate(numeros, operator.mul)
print("Accumulate sem função")
print(list(acumulado_soma))
print("Accumulate com multiplicação")
print(list(acumulado_multiplicacao))
Saída:
Accumulate sem função
[1, 3, 6, 10, 15]
Accumulate com multiplicação
[1, 2, 6, 24, 120]
chain()
chain(iterable_1, iterable_2, ...)
recebe vários iteráveis e os encadeia, gerando um único iterável que contém os valores de todos os iteráveis passados para a função chain()
.
import itertools
letras = ['A', 'B', 'C', 'D']
numeros = [1, 2, 3]
cores = ['vermelho', 'verde', 'amarelo']
# Encadeando letras, números e cores juntos
encadeado_iteravel = list(itertools.chain(letras, numeros, cores))
print(encadeado_iteravel)
Saída:
['A', 'B', 'C', 'D', 1, 2, 3, 'vermelho', 'verde', 'amarelo']
chain.from_iterable()
chain.from_iterable(iterable)
é semelhante a chain()
. No entanto, difere, pois recebe apenas um único iterável que contém sub-iteráveis e os encadeia.
import itertools
letras = ['A', 'B', 'C', 'D']
numeros = [1, 2, 3]
cores = ['vermelho', 'verde', 'amarelo']
iteravel = ['ola', cores, letras, numeros]
corrente = list(itertools.chain.from_iterable(iteravel))
print(corrente)
Saída:
['o', 'l', 'a', 'vermelho', 'verde', 'amarelo', 'A', 'B', 'C', 'D', 1, 2, 3]
compress()
compress(data, selectors)
aceita dois argumentos: data
, que é um iterável, e selectors
, que é um iterável que contém valores booleanos (True
e False
). 1
e 0
também podem ser usados como alternativas para os valores booleanos True
e False
. A função compress()
filtra os elementos de data
, usando os elementos correspondentes passados em selectors
.
Os valores em data
que correspondem a True
ou 1
em selectors
são selecionados, enquanto aqueles que correspondem a False
ou 0
são ignorados. Se você passar menos booleanos em selectors
do que o número de itens em data
, todos os elementos além dos booleanos passados em selectors
serão ignorados.
import itertools
# data tem 10 itens
data = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
# passando 9 seletores
seletores = [True, False, 1, False, 0, 1, True, False, 1]
# Selecionar elementos de data com base em seletores
filtrado_data = list(itertools.compress(data, seletores))
print(filtrado_data)
Saída:
['A', 'C', 'F', 'G', 'I']
dropwhile()
dropwhile(function, sequence)
recebe uma função (com uma condição que retorna True
ou False
) e uma sequência de valores. Em seguida, descarta todos os valores até que a condição passada retorne False
. Uma vez que a condição retorne False
, o restante dos elementos serão incluídos nos resultados, independentemente de retornarem True
ou False
.
import itertools
numeros = [1, 2, 3, 4, 5, 1, 6, 7, 2, 1, 8, 9, 0, 7]
# Descartar elementos até que a condição passada seja False
filtrado_numeros = list(itertools.dropwhile(lambda x: x < 5, numeros))
print(filtrado_numeros)
Saída:
[5, 1, 6, 7, 2, 1, 8, 9, 0, 7]
filterfalse()
filterfalse(function, sequence)
recebe uma função (com uma condição que é avaliada como True
ou False
) e uma sequência. Em seguida, retorna os valores da sequência que não satisfazem a condição da função.
import itertools
numeros = [1, 2, 3, 4, 2, 3, 5, 6, 5, 8, 1, 2, 3, 6, 2, 7, 4, 3]
# Filtrar elementos para os quais a condição é False
filtrado_numeros = list(itertools.filterfalse(lambda x: x < 4, numeros))
print(filtrado_numeros)
Saída:
[4, 5, 6, 5, 8, 6, 7, 4]
groupby()
groupby(iterable, key)
recebe um iterável e uma chave, e então cria um iterador que retorna chaves e grupos consecutivos. Para que funcione, o iterável passado para ele precisa ser classificado pela mesma função de chave. A função de chave calcula um valor de chave para cada elemento no iterável.
import itertools
lista_entrada = [("Doméstico", "Vaca"), ("Doméstico", "Cachorro"), ("Doméstico", "Gato"), ("Selvagem", "Leão"), ("Selvagem", "Zebra"), ("Selvagem", "Elefante")]
classificacao = itertools.groupby(lista_entrada,lambda x: x[0])
for chave, valor in classificacao:
print(chave, ":", list(valor))
Saída:
Doméstico : [('Doméstico', 'Vaca'), ('Doméstico', 'Cachorro'), ('Doméstico', 'Gato')]
Selvagem : [('Selvagem', 'Leão'), ('Selvagem', 'Zebra'), ('Selvagem', 'Elefante')]
islice()
islice(iterable, start, stop, step)
permite fatiar um iterável usando os valores start
, stop
e step
. O argumento step
é opcional. A contagem começa em 0 e o item na posição de parada não é incluído.
import itertools
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
# Selecionar elementos dentro de um intervalo
selecionado_numeros = list(itertools.islice(numeros, 2, 10))
selecionado_numeros_step = list(itertools.islice(numeros, 2, 10,2))
print("islice sem definir um valor de step")
print(selecionado_numeros)
print("islice com um valor de step de 2")
print(selecionado_numeros_step)
Saída:
islice sem definir um valor de step
[3, 4, 5, 6, 7, 8, 9, 10]
islice com um valor de step de 2
[3, 5, 7, 9]
pairwise()
pairwise(iterable)
retorna pares sobrepostos sucessivos retirados do iterável, na ordem em que aparecem no iterável. Se o iterável tiver menos de dois valores, o resultado de pairwise()
estará vazio.
from itertools import pairwise
numeros = [1, 2, 3, 4, 5, 6, 7, 8]
palavra = 'MUNDO'
unico = ['A']
print(list(pairwise(numeros)))
print(list(pairwise(palavra)))
print(list(pairwise(unico)))
Saída:
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)]
[('M', 'U'), ('U', 'N'), ('N', 'D'), ('D', 'O')]
[]
starmap()
starmap(function, iterable)
é usada em vez de map()
quando os argumentos da função já estão agrupados em tuplas. A função starmap()
aplica uma função aos elementos do iterável passado. O iterável deve ter elementos agrupados em tuplas.
import itertools
iter_starmap = [(123, 63, 13), (5, 6, 52), (824, 51, 9), (26, 24, 16), (14, 15, 11)]
print (list(itertools.starmap(min, iter_starmap)))
Saída:
[13, 5, 9, 16, 11]
takewhile()
takewhile(function, iterable)
funciona de maneira oposta a dropwhile()
. takewhile()
recebe uma função com uma condição a ser avaliada e um iterável. Em seguida, inclui todos os elementos no iterável que satisfazem a condição até que False
seja retornado. Depois que False
for retornado, todos os elementos seguintes no iterável serão ignorados.
import itertools
numeros = [1, 2, 3, 4, 5, 1, 6, 7, 2, 1, 8, 9, 0, 7]
# Incluir elementos até que a condição seja False
filtrado_numeros = list(itertools.takewhile(lambda x: x < 5, numeros))
print(filtrado_numeros)
Saída:
[1, 2, 3, 4]
tee()
tee(iterable, n)
recebe um iterável e retorna vários iteradores independentes. O número de iteradores a serem retornados é definido por n
, cujo valor padrão é 2.
import itertools
numeros = [1, 2, 3, 4, 5]
# Criar dois iteradores independentes a partir de numeros
iter1, iter2 = itertools.tee(numeros, 2)
print(list(iter1))
print(list(iter2))
Saída:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
zip_longest()
zip_longest(iterables, fillvalue)
aceita vários iteradores e um fillvalue
. Em seguida, retorna um iterador que agrega elementos de cada um dos iteradores. Se os iteradores não tiverem o mesmo comprimento, os valores ausentes serão preenchidos com o fillvalue
até que o iterável mais longo seja esgotado.
import itertools
nomes = ['João', 'Mateus', 'Maria', 'Alice', 'Bob', 'Charlie', 'Fúria']
idades = [25, 30, 12, 13, 42]
# Combinar nomes e idades, preenchendo as idades ausentes com um traço
combinado = itertools.zip_longest(nomes, idades, fillvalue="-")
for nome, idade in combinado:
print(nome, idade)
Saída:
João 25
Mateus 30
Maria 12
Alice 13
Bob 42
Charlie -
Fúria -
Conclusão
O módulo itertools
do Python oferece um conjunto de ferramentas muito importante para desenvolvedores. O itertools
é amplamente utilizado em programação funcional, no processamento e transformação de dados, na filtragem e seleção de dados, no agrupamento e agregação, na combinação de iteráveis, em combinatória e ao trabalhar com sequências infinitas.
Como desenvolvedor Python, você se beneficiará muito ao aprender sobre o itertools
, portanto, utilize este artigo para se familiarizar com o módulo.