Czym są funkcje Pythona Itertools?

Zgodnie z dokumentacją Pythona, Itertools to moduł Pythona, który zapewnia zestaw szybkich i oszczędzających pamięć narzędzi do pracy z iteratorami Pythona. Narzędzia te mogą być używane samodzielnie lub w połączeniu i umożliwiają zwięzłe i wydajne tworzenie i pracę z iteratorami w szybki i efektywny pod względem pamięci sposób.

Moduł Itertools zawiera funkcje ułatwiające pracę z iteratorami, szczególnie przy obsłudze dużych zbiorów danych. Funkcje Itertools mogą działać na istniejących iteratorach, tworząc jeszcze bardziej złożone iteratory Pythona.

Ponadto Itertools może pomóc programistom zmniejszyć liczbę błędów podczas pracy z iteratorami i pisać czystszy, czytelny i łatwy w utrzymaniu kod.

W oparciu o funkcjonalność, jaką zapewniają iteratory w module Itertools, można je podzielić na następujące typy:

# 1. Nieskończone iteratory

Są to iteratory, które pozwalają pracować z nieskończonymi sekwencjami i uruchamiać pętlę w nieskończoność, jeśli nie ma warunku wyjścia z pętli. Takie iteratory są przydatne podczas symulacji nieskończonych pętli lub generowania nieograniczonej sekwencji. Itertools ma trzy nieskończone iteratory, które obejmują count(), cycle() i repeat().

#2. Iteratory kombinatoryczne

Iteratory kombinatoryczne obejmują funkcje, których można używać do pracy na iloczynach kartezjańskich oraz do wykonywania kombinacji i permutacji elementów zawartych w iterowalnym. Są to podstawowe funkcje, gdy próbujesz znaleźć wszystkie możliwe sposoby ułożenia lub połączenia elementów w iterowalnym. Itertools ma cztery iteratory kombinatoryczne. Są to produkt(), permutacje(), kombinacje() i kombinacje_z_zamianą().

#3. Iteratory kończące się na najkrótszej sekwencji wejściowej

Są to iteratory kończące, które są używane na skończonych sekwencjach i generują dane wyjściowe na podstawie typu użytej funkcji. Przykłady takich iteratorów kończących to: accept(), chain(), chain.from_iterable(), compress(), dropwhile(), filterfalse(), groupby(), islice(), pairwise(), starmap(), takewhile (), tee() i zip_najdłuższy().

Przyjrzyjmy się, jak działają różne funkcje Itertools w zależności od ich typu:

Nieskończone iteratory

Trzy nieskończone iteratory obejmują:

# 1. liczyć()

Funkcja count(start, step) generuje nieskończoną sekwencję liczb zaczynając od wartości początkowej. Funkcja przyjmuje dwa opcjonalne argumenty: start i krok. Argument start ustawia miejsce, w którym sekwencja liczb powinna się zaczynać. Domyślnie zaczyna się od 0, jeśli nie podano wartości początkowej. krok ustawia różnicę między każdą kolejną liczbą. Domyślna wartość kroku to 1.

import itertools
# count starting at 4, making steps of 2  
for i in itertools.count(4, 2):
    # condition to end the loop avoiding infinite looping
    if i == 14:
        break
    else:
        print(i) # output - 4, 6, 8, 10, 12

Wyjście

4
6
8
10
12

#2. cykl()

cycle(iterable) funkcja przyjmuje iterowalny jako argument, a następnie cyklicznie przechodzi przez iterowalny, umożliwiając dostęp do elementów w iterowalnym porządku w kolejności, w jakiej się pojawiają.

Na przykład, jeśli przechodzimy [“red”, “green”, “yellow”] do cyklu(), w pierwszym cyklu będziemy mieli dostęp do „czerwonego”; w drugim cyklu będziemy mieli dostęp do „zielonego”, potem „żółtego”. W czwartym cyklu, ponieważ wszystkie elementy zostały wyczerpane w iterowalności, zaczniemy od „czerwonego” i będziemy kontynuować w nieskończoność.

Wywołując cykl () przechowujesz jego wynik w zmiennej, aby utworzyć iterator, który utrzymuje swój stan. Dzięki temu cykl nie zaczyna się za każdym razem od nowa, dając dostęp tylko do pierwszego elementu.

import itertools

colors = ["red", "green", "yellow"]
# pass in colors into cycle()
color_cycle = itertools.cycle(colors)
print(color_cycle)

# range used to stop the infinite loop once we've printed 7 times
# next() used to return the next item from the iterator
for i in range(7):
    print(next(color_cycle))

Wyjście:

red
green
yellow
red
green
yellow
red

#3. powtarzać()

repeat(elem,n) przyjmuje dwa argumenty, element do powtórzenia (elem) oraz liczbę powtórzeń elementu (n). Element, który chcesz powtórzyć, może być pojedynczą wartością lub elementem iterowalnym. Jeśli nie podasz n, element będzie powtarzany w nieskończoność.

import itertools
   
for i in itertools.repeat(10, 3):
    print(i)

Wyjście:

10 
10
10

Iteratory kombinatoryczne

Iteratory kombinatoryczne obejmują:

# 1. produkt()

product() to funkcja służąca do obliczania iloczynu kartezjańskiego iterowalności przekazanej do niej. Jeśli mamy dwa zbiory iterowalne, na przykład x = {7,8} i y = {1,2,3}, iloczyn kartezjański x i y będzie zawierał wszystkie możliwe kombinacje elementów z x i y, gdzie pierwszy element pochodzi z x, a drugi z y. Iloczyn kartezjański x i y w tym przypadku wynosi [(7, 1), (7, 2), (7, 3), (8, 1), (8, 2), (8, 3)].

product() przyjmuje opcjonalny parametr o nazwie repeat, który jest używany do obliczania iloczynu kartezjańskiego iterowalnego z samym sobą. repeat określa liczbę powtórzeń dla każdego elementu z iteracji wejściowych podczas obliczania iloczynu kartezjańskiego.

Na przykład wywołanie product(’ABCD’, repeat=2) daje kombinacje takie jak (’A’, 'A’), (’A’, 'B’), (’A’, 'C’) itd. NA. Jeśli powtórzenie zostało ustawione na 3, funkcja zwróciłaby kombinacje, takie jak (’A’, 'A’, 'A’), (’A’, 'A’, 'B’), (’A’, 'A’ , „C”), („A”, „A”, „D”) i tak dalej.

from itertools import product
# product() with the optional repeat argument
print("product() with the optional repeat argument ")
print(list(product('ABC', repeat = 2)))

# product with no repeat
print("product() WITHOUT an optional repeat argument")
print(list(product([7,8], [1,2,3])))

Wyjście

product() with the optional repeat argument 
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]
product() WITHOUT an optional repeat argument
[(7, 1), (7, 2), (7, 3), (8, 1), (8, 2), (8, 3)]

#2. permutacje()

permutations(iterable, group_size) zwraca wszystkie możliwe permutacje przekazanej do niej iterowalności. Permutacja reprezentuje liczbę sposobów uporządkowania elementów w zbiorze. permutations() przyjmuje opcjonalny argument rozmiar_grupy. Jeśli rozmiar_grupy nie zostanie określony, wygenerowane permutacje będą miały taki sam rozmiar, jak długość elementu iterowalnego przekazanego do funkcji

import itertools
numbers = [1, 2, 3]
sized_permutations = list(itertools.permutations(numbers,2))
unsized_permuatations = list(itertools.permutations(numbers))

print("Permutations with a size of 2")
print(sized_permutations)
print("Permutations with NO size argument")
print(unsized_permuatations)

Wyjście

Permutations with a group size of 2
[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
Permutations with NO size argument
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

#3. kombinacje()

Combines(iterable, size) zwraca wszystkie możliwe kombinacje elementu iterowalnego o danej długości z elementów elementu iterowalnego przekazanego do funkcji. Argument size określa rozmiar każdej kombinacji.

Wyniki są uporządkowane. Kombinacja różni się nieco od permutacji. W przypadku permutacji kolejność ma znaczenie, ale w przypadku kombinacji kolejność nie ma znaczenia. Na przykład w [A, B, C] istnieje 6 permutacji: AB, AC, BA, BC, CA, CB, ale tylko 3 kombinacje AB, AC, BC.

import itertools
numbers = [1, 2, 3,4]
size2_combination = list(itertools.combinations(numbers,2))
size3_combination = list(itertools.combinations(numbers, 3))

print("Combinations with a size of 2")
print(size2_combination)
print("Combinations with a size of 3")
print(size3_combination)

Wyjście:

Combinations with a size of 2
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
Combinations with a size of 3
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]

#4. kombinacje_z_wymianą()

Combines_with_replacement(iterable, size) generuje wszystkie możliwe kombinacje iterowalności o danej długości z iterowalności przekazanej do funkcji i dopuszcza powtarzające się elementy w kombinacjach wyjściowych. Rozmiar określa rozmiar generowanych kombinacji.

Ta funkcja różni się od kombinacji() tym, że daje kombinacje, w których element może zostać powtórzony więcej niż jeden raz. Na przykład możesz uzyskać kombinację taką jak (1,1), której nie możesz uzyskać za pomocą kombinacji().

import itertools
numbers = [1, 2, 3,4]

size2_combination = list(itertools.combinations_with_replacement(numbers,2))
print("Combinations_with_replacement => size 2")
print(size2_combination)

Wyjście

Combinations_with_replacement => size 2
[(1, 1), (1, 2), (1, 3), (1, 4), (2, 2), (2, 3), (2, 4), (3, 3), (3, 4), (4, 4)]

Zakończenie iteratorów

Obejmuje to iteratory, takie jak:

# 1. gromadzić()

akumuluj(iterowalna, funkcja) przyjmuje iterowalny i drugi opcjonalny argument, który jest funkcją. Następnie zwraca skumulowany wynik zastosowania funkcji w każdej iteracji na elementach iterowalnych. Jeśli żadna funkcja nie zostanie przekazana, wykonywane jest dodawanie i zwracane są skumulowane wyniki.

import itertools
import operator
numbers = [1, 2, 3, 4, 5]

# Accumulate the sum of numbers
accumulated_val = itertools.accumulate(numbers)
accumulated_mul = itertools.accumulate(numbers, operator.mul)
print("Accumulate with no function")
print(list(accumulated_val))
print("Accumulate with multiplication")
print(list(accumulated_mul))

Wyjście:

Accumulate with no function
[1, 3, 6, 10, 15]
Accumulate with multiplication
[1, 2, 6, 24, 120]

#2. łańcuch()

chain(iterable_1, iterable_2, …) bierze wiele iteracji i łączy je razem, tworząc pojedynczą iterowalność zawierającą wartości z iteracji przekazanych do funkcji chain()

import itertools

letters = ['A', 'B', 'C', 'D']
numbers = [1, 2, 3]
colors = ['red', 'green', 'yellow']

# Chain letters and numbers together
chained_iterable = list(itertools.chain(letters, numbers, colors))
print(chained_iterable)

Wyjście:

['A', 'B', 'C', 'D', 1, 2, 3, 'red', 'green', 'yellow']

#3. łańcuch.z_iterable()

chain.from_iterable(iterable) ta funkcja jest podobna do chain(). Jednak różni się od łańcucha tym, że bierze tylko jedną iterowalną zawierającą pod-iterable i łączy je razem.

import itertools

letters = ['A', 'B', 'C', 'D']
numbers = [1, 2, 3]
colors = ['red', 'green', 'yellow']

iterable = ['hello',colors, letters, numbers]
chain = list(itertools.chain.from_iterable(iterable))
print(chain)

Wyjście:

['h', 'e', 'l', 'l', 'o', 'red', 'green', 'yellow', 'A', 'B', 'C', 'D', 1, 2, 3]

#4. Kompresja()

compress(dane, selektory) przyjmuje dwa argumenty, dane, które są iterowalne, oraz selektory, które są iterowalne i zawierają wartości logiczne true i false. 1, 0 mogą być również używane jako alternatywy dla wartości boolowskich prawda i fałsz. compress() następnie filtruje przekazane dane przy użyciu odpowiednich elementów przekazanych w selektorze.

Wartości w danych odpowiadające wartości true lub 1 w selektorze są wybierane, podczas gdy pozostałe odpowiadające wartości false lub 0 są ignorowane. Jeśli przekażesz mniej wartości logicznych w selektorach niż liczba elementów w danych, wszystkie elementy poza przekazanymi wartościami boolowskimi w selektorach zostaną zignorowane

import itertools

# data has 10 items
data = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
# passing in 9 selector items
selectors = [True, False, 1, False, 0, 1, True, False, 1]

# Select elements from data based on selectors
filtered_data = list(itertools.compress(data, selectors))
print(filtered_data)

Wyjście:

['A', 'C', 'F', 'G', 'I']

#5. dropwhile()

dropwhile(funkcja, sekwencja) przyjmuje funkcję z warunkiem zwracającym prawdę lub fałsz oraz sekwencję wartości. Następnie odrzuca wszystkie wartości, dopóki spełniony warunek nie zwróci False. Gdy warunek zwróci wartość false, pozostałe elementy zostaną uwzględnione w wynikach, niezależnie od tego, czy zwrócą wartość True, czy False.

import itertools

numbers = [1, 2, 3, 4, 5, 1, 6, 7, 2, 1, 8, 9, 0, 7]

# Drop elements until the passed condition is False
filtered_numbers = list(itertools.dropwhile(lambda x: x < 5, numbers))
print(filtered_numbers)

Wyjście:

[5, 1, 6, 7, 2, 1, 8, 9, 0, 7]

#6. filtruj fałsz()

filterfalse(funkcja, sekwencja) przyjmuje funkcję z warunkiem, którego wynikiem jest prawda lub fałsz, oraz sekwencją. Następnie zwraca wartości z sekwencji, które nie spełniają warunku w funkcji.

import itertools

numbers = [1, 2, 3, 4, 2, 3 5, 6, 5, 8, 1, 2, 3, 6, 2, 7, 4, 3]

# Filter elements for which condition is False
filtered_numbers = list(itertools.filterfalse(lambda x: x < 4, numbers))
print(filtered_numbers)

Wyjście:

[4, 5, 6, 5, 8, 6, 7, 4]

#7. Grupuj według()

groupby(iterable, key) pobiera iterowalny i klucz, a następnie tworzy iterator, który zwraca kolejne klucze i grupy. Aby to zadziałało, przekazana do niego iteracja musi być posortowana według tej samej funkcji klucza. Kluczowa funkcja komputera jest kluczową wartością dla każdego elementu w iterowalnym.

import itertools

input_list = [("Domestic", "Cow"), ("Domestic", "Dog"), ("Domestic", "Cat"),("Wild", "Lion"), ("Wild", "Zebra"), ("Wild", "Elephant")]
classification = itertools.groupby(input_list,lambda x: x[0])
for key,value in classification:
  print(key,":",list(value))

Wyjście:

Domestic : [('Domestic', 'Cow'), ('Domestic', 'Dog'), ('Domestic', 'Cat')]
Wild : [('Wild', 'Lion'), ('Wild', 'Zebra'), ('Wild', 'Elephant')]

#8. islice()

islice(iterable, start, stop, step) pozwala ci podzielić iterowalny przy użyciu przekazanych wartości start, stop i step. Argument kroku jest opcjonalny. Liczenie rozpoczyna się od 0, a pozycja na numerze przystanku nie jest uwzględniana.

import itertools

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

# Select elements within a range
selected_numbers = list(itertools.islice(numbers, 2, 10))
selected_numbers_step= list(itertools.islice(numbers, 2, 10,2))
print("islice without setting a step value")
print(selected_numbers)
print("islice with a step value of 2")
print(selected_numbers_step)

Wyjście:

islice without setting a step value
[3, 4, 5, 6, 7, 8, 9, 10]
islice with a step value of 2
[3, 5, 7, 9]

#9. parami()

pairwise(iterable) zwraca kolejne nakładające się pary pobrane z iterowalnego przekazanego do niej w kolejności, w jakiej pojawiają się w iterowalnym. Jeśli przekazana do niego iterowalność ma mniej niż dwie wartości, wynik z pairwise() będzie pusty.

from itertools import pairwise

numbers = [1, 2, 3, 4, 5, 6, 7, 8]
word = 'WORLD'
single = ['A']

print(list(pairwise(numbers)))
print(list(pairwise(word)))
print(list(pairwise(single)))

Wyjście:

[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)]
[('W', 'O'), ('O', 'R'), ('R', 'L'), ('L', 'D')]
[]

#10. mapa gwiezdna()

starmap(function, iterable) to funkcja używana zamiast map(), gdy parametry argumentów są już zgrupowane w krotki. startmap() stosuje funkcję do elementów przekazanej do niej iterowalności. Iterowalny powinien mieć elementy pogrupowane w krotki.

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)))

Wyjście:

[13, 5, 9, 16, 11]

#11. na chwilę()

takewhile(funkcja, iterowalność) działa w odwrotny sposób niż dropwhile(). takewhile() przyjmuje funkcję z warunkiem do oceny i iteracją. Następnie zawiera wszystkie elementy iterowalne, które spełniają warunek w funkcji, dopóki nie zostanie zwrócona wartość False. Po zwróceniu wartości False wszystkie następujące elementy w iterowalności są ignorowane.

import itertools

numbers = [1, 2, 3, 4, 5, 1, 6, 7, 2, 1, 8, 9, 0, 7]

# Drop elements until the passed condition is False
filtered_numbers = list(itertools.takewhile(lambda x: x < 5, numbers))
print(filtered_numbers)

Wyjście:

[1, 2, 3, 4]

#12. trójnik()

tee(iterable, n) przyjmuje iterowalny i zwraca wiele niezależnych iteratorów. Liczba zwracanych iteratorów jest ustawiana przez n, które domyślnie wynosi 2.

import itertools

numbers = [1, 2, 3, 4, 5]

# Create two independent iterators from numbers
iter1, iter2 = itertools.tee(numbers, 2)
print(list(iter1))
print(list(iter2))

Wyjście:

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

#13. zip_najdłuższy()

zip_longest(iterables, fillvalue) przyjmuje wiele iteratorów i wartość wypełnienia. Następnie zwraca iterator, który agreguje elementy z każdego z przekazanych mu iteratorów. Jeśli iteratory nie mają tej samej długości, brakujące wartości są zastępowane przez wartość wypełnienia przekazywaną do funkcji, aż do wyczerpania najdłuższej iterowalności.

import itertools

names = ['John', 'mathew', 'mary', 'Alice', 'Bob', 'Charlie', 'Fury']
ages = [25, 30, 12, 13, 42]

# Combine name and ages, filling in missing ages with a dash
combined = itertools.zip_longest(names, ages, fillvalue="-")

for name, age in combined:
    print(name, age)

Wyjście:

John 25
mathew 30
mary 12
Alice 13
Bob 42
Charlie -
Fury -

Wniosek

Python itertools to ważny zestaw narzędzi dla programisty Pythona. Python itertools są szeroko stosowane w programowaniu funkcjonalnym, przetwarzaniu i transformacji danych, filtrowaniu i selekcji danych, grupowaniu i agregacji, łączeniu iteracji, kombinatoryce oraz podczas pracy z nieskończonymi sekwencjami.

Jako programista Pythona wiele zyskasz, poznając itertools, więc skorzystaj z tego artykułu, aby zapoznać się z Python Itertools.