Pular para o conteúdo

Como Usar a biblioteca itertools do Python?

[

Itertools em Python 3, Por Exemplo

O que é Itertools e por que você deve usá-lo?

De acordo com a documentação do itertools, ele é um módulo que implementa um conjunto de blocos de construção de iteradores inspirados nas construções de APL, Haskell e SML… Juntos, eles formam uma “álgebra de iteradores” que torna possível construir ferramentas especializadas de forma concisa e eficiente em Python puro.

Isso significa, basicamente, que as funções do itertools “operam” em iteradores para produzir iteradores mais complexos. Um exemplo é a função embutida zip(), que recebe qualquer número de iteráveis como argumento e retorna um iterador sobre tuplas dos elementos correspondentes:

>>> list(zip([1, 2, 3], ['a', 'b', 'c']))
[(1, 'a'), (2, 'b'), (3, 'c')]

Como exatamente o zip() funciona?

[1, 2, 3] e ['a', 'b', 'c'], assim como todas as listas, são iteráveis, o que significa que eles podem retornar seus elementos um por vez. Tecnicamente, qualquer objeto Python que implemente os métodos .__iter__() ou .__getitem__() é iterável. (Veja a introdução aos iteradores e iteráveis em Python)

Dentro da função zip(), dois iteradores são criados a partir das listas de entrada. Esses iteradores são então percorridos simultaneamente, e em cada iteração um par de elementos é colocado em uma tupla, que é retornada pelo iterador resultante. O resultado é uma lista de tuplas correspondentes dos elementos das listas originais.

O módulo itertools estende esse conceito de composição de iteradores para uma coleção de funções poderosas que podem ser usadas para resolver uma ampla variedade de problemas comuns de programação de maneira eficiente.

Neste artigo, vamos explorar o módulo itertools através de exemplos práticos e construídos de forma progressiva. Vamos começar com exemplos simples e gradualmente aumentar a complexidade. Vamos ver como usar essas funções do itertools e como combiná-las para criar código eficiente e elegante.

Antes de prosseguir, é importante ressaltar que este artigo é voltado para programadores Python intermediários e avançados. É necessário ter um bom entendimento de iteradores e geradores em Python 3, atribuição múltipla e desempacotamento de tuplas. Se você não está familiarizado com esses conceitos ou precisa relembrá-los, recomendamos a leitura dos seguintes recursos:

Agora que estamos prontos, vamos começar com a primeira seção:

O Recurso ‘zip()‘

A função zip() é útil quando você precisa combinar elementos de várias listas ou iteráveis ​​em uma única estrutura de dados. Por exemplo, suponha que você tenha duas listas: uma com nomes de pessoas e outra com suas respectivas idades. Você pode usar a função zip() para criar uma lista de tuplas, onde cada tupla contém o nome e a idade correspondente de uma pessoa:

nomes = ['João', 'Maria', 'Pedro']
idades = [25, 30, 35]
pessoas = list(zip(nomes, idades))
print(pessoas)
# Output: [('João', 25), ('Maria', 30), ('Pedro', 35)]

Observe que a função zip() retorna um objeto iterável, então usamos a função list() para converter esse objeto em uma lista. O resultado é uma lista de tuplas, onde cada tupla contém um nome e uma idade correspondente.

Além disso, a função zip() pode lidar com um número variável de iteráveis. Por exemplo, você pode combinar três listas diferentes:

nomes = ['João', 'Maria', 'Pedro']
idades = [25, 30, 35]
salarios = [5000, 6000, 7000]
pessoas = list(zip(nomes, idades, salarios))
print(pessoas)
# Output: [('João', 25, 5000), ('Maria', 30, 6000), ('Pedro', 35, 7000)]

Nesse caso, cada tupla gerada contém um nome, uma idade e um salário correspondente.

Agora que você entende como usar a função zip(), vamos explorar outras funcionalidades do itertools na próxima seção.

A Combinatória de Iteradores

Ao trabalhar com iteradores, um problema comum é gerar todas as combinações possíveis dos elementos de um ou mais iteráveis. Essas combinações podem ser úteis para resolver problemas de combinatória, como jogos de cartas, quebra-cabeças, problemas de alocação, entre outros.

O módulo itertools fornece várias funções voltadas para a combinação e permutação de iteráveis. Vamos explorar algumas dessas funções a seguir.

Cartesian Product com a Função ‘product()‘

A função product() gera o produto cartesiano de dois ou mais iteráveis. O produto cartesiano de dois conjuntos A e B é definido como o conjunto de todas as possíveis combinações de um elemento de A com um elemento de B.

Vamos ver um exemplo usando duas listas:

from itertools import product
a = [1, 2]
b = ['a', 'b', 'c']
resultado = list(product(a, b))
print(resultado)
# Output: [(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c')]

A função product() retorna um iterador que gera tuplas contendo todos os pares possíveis de elementos das listas a e b. Nesse exemplo, temos 2 elementos em a e 3 elementos em b, então o produto cartesiano resultante contém 2 x 3 = 6 tuplas.

Você também pode usar a função product() com o mesmo iterável várias vezes. Veja o exemplo a seguir com uma única lista:

from itertools import product
a = [1, 2]
resultado = list(product(a, repeat=3))
print(resultado)
# Output: [(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2)]

Nesse caso, o iterador gerado pela função product(a, repeat=3) gera tuplas contendo todas as combinações de 3 elementos de a. Como a possui 2 elementos, o resultado é uma lista com 2^3 = 8 tuplas.

A função product() é útil em situações em que você precisa gerar todas as combinações possíveis dos elementos de um ou mais iteráveis. Você pode usar essas combinações para resolver problemas de forma sistemática e abrangente.

Permutações com a Função ‘permutations()‘

A função permutations() gera todas as permutações possíveis dos elementos de um iterável. Uma permutação é uma reorganização dos elementos de um conjunto em uma ordem específica.

Vamos ver um exemplo usando uma lista:

from itertools import permutations
a = [1, 2, 3]
resultado = list(permutations(a))
print(resultado)
# Output: [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

A função permutations() retorna um iterador que gera tuplas contendo todas as permutações possíveis dos elementos da lista a. Nesse exemplo, a lista a possui 3 elementos, então o resultado contém 3! = 6 tuplas.

Por padrão, a função permutations() gera permutações sem repetição, ou seja, as tuplas resultantes não contêm elementos repetidos. Se você deseja gerar permutações com repetição, pode usar o parâmetro repeat. Veja um exemplo:

from itertools import permutations
a = [1, 2, 3]
resultado = list(permutations(a, 2))
print(resultado)
# Output: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

Aqui, a função permutations(a, 2) gera todas as permutações de 2 elementos da lista a. A lista resultante contém 3!/1! = 6 tuplas.

A função permutations() é útil quando você precisa explorar todas as permutações possíveis de um conjunto de elementos. Você pode usar essas permutações para resolver problemas relacionados a ordem, como anagramas, códigos de permutação, entre outros.

Combinações com a Função ‘combinations()‘

A função combinations() gera todas as combinações possíveis de um tamanho específico dos elementos de um iterável. Uma combinação é uma seleção de elementos sem levar em conta a ordem.

Vamos ver um exemplo usando uma lista:

from itertools import combinations
a = [1, 2, 3, 4]
resultado = list(combinations(a, 2))
print(resultado)
# Output: [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]

A função combinations() retorna um iterador que gera tuplas contendo todas as combinações possíveis de 2 elementos da lista a. Nesse exemplo, a lista a possui 4 elementos, então o resultado contém C(4, 2) = 6 tuplas.

A função combinations() também pode gerar todas as combinações possíveis sem levar em conta o tamanho das combinações. Você só precisa omitir o segundo argumento:

from itertools import combinations
a = [1, 2, 3, 4]
resultado = list(combinations(a))
print(resultado)
# Output: [(1,), (2,), (3,), (4,), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4), (1, 2, 3, 4)]

Nesse caso, a função combinations(a) gera todas as combinações possíveis de elementos da lista a, independentemente do tamanho. O resultado é uma lista com 2^4 = 16 tuplas.

A função combinations() é útil quando você precisa explorar todas as combinações possíveis de um conjunto de elementos, sem levar em conta a ordem. Você pode usar essas combinações para resolver problemas relacionados a seleção, agrupamento e combinação de elementos.

Combinações com Repetição com a Função ‘combinations_with_replacement()‘

A função combinations_with_replacement() gera todas as combinações possíveis de um tamanho específico dos elementos de um iterável, permitindo a repetição de elementos. Ou seja, ela gera combinações que podem conter elementos repetidos.

Vamos ver um exemplo usando uma lista:

from itertools import combinations_with_replacement
a = [1, 2, 3]
resultado = list(combinations_with_replacement(a, 2))
print(resultado)
# Output: [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

A função combinations_with_replacement() retorna um iterador que gera tuplas contendo todas as combinações possíveis com repetição de 2 elementos da lista a. Nesse exemplo, a lista a possui 3 elementos, então o resultado contém C(3 + 2 - 1, 2) = 6 tuplas.

A função combinations_with_replacement() é útil quando você precisa gerar todas as combinações possíveis de um tamanho específico, permitindo a repetição de elementos. Você pode usar essas combinações para resolver problemas relacionados a amostras com reposição, sequências de caracteres disponíveis, entre outros.

Comparação entre ‘permutations()’ e ‘combinations()‘

Embora tanto a função permutations() quanto a função combinations() gerem combinações de elementos de iteráveis, elas são diferentes em termos de ordem e de repetição.

A função permutations() leva em conta a ordem dos elementos, o que significa que duas permutações são consideradas diferentes se a ordem dos elementos for diferente. Por exemplo, as permutações (1, 2, 3) e (3, 2, 1) são consideradas diferentes.

Já a função combinations() não leva em conta a ordem dos elementos, o que significa que duas combinações são consideradas iguais se contiverem os mesmos elementos independentemente da ordem. Por exemplo, as combinações (1, 2) e (2, 1) são consideradas iguais.

Além disso, a função permutations() não permite a repetição de elementos, enquanto a função combinations() não permite a repetição de elementos em uma única combinação.

A escolha entre permutations() e combinations() depende do problema que você está tentando resolver. Se a ordem dos elementos importa e você não quer elementos repetidos, use permutations(). Caso contrário, se a ordem dos elementos não importa e você quer combinações sem repetição, use combinations().

Conclusão

O módulo itertools é uma ferramenta poderosa para lidar com iterações de forma eficiente e elegante em Python. Neste artigo, você aprendeu sobre algumas das funções mais úteis do itertools para combinar e permutar elementos de iteráveis. Essas funções podem ajudar a resolver uma variedade de problemas comuns de programação, desde a combinação de elementos até a geração de permutações.

Se você quer se aprofundar ainda mais no assunto, recomendamos explorar a documentação oficial do itertools. Lá você encontrará informações detalhadas sobre todas as funções disponíveis, bem como exemplos de uso.

Esperamos que este artigo tenha sido útil e que você possa explorar todo o potencial do módulo itertools em seus projetos Python. Experimente as funções apresentadas aqui e veja como elas podem facilitar e otimizar seu código. Divirta-se programando!