Згідно з офіційною документацією Python, модуль `itertools` надає розробникам широкий спектр ефективних та швидких інструментів для роботи з ітераторами. Ці інструменти можна використовувати як окремо, так і комбінувати для створення складних ітерацій, що дозволяє обробляти великі обсяги даних зі значною економією пам’яті.
Модуль `itertools` включає функції, що полегшують маніпуляції з ітераторами, особливо при роботі з великими наборами даних. Ці функції можуть використовуватись для перетворення існуючих ітераторів на більш складні структури.
Крім того, використання `itertools` допомагає зменшити кількість помилок під час обробки ітерацій, сприяючи написанню більш зрозумілого, легкого для читання та підтримки коду.
Функції, що надаються модулем `itertools`, можна розподілити на три основні категорії:
#1. Нескінченні ітератори
Ці ітератори призначені для роботи з нескінченними послідовностями. Їх використання може призвести до нескінченного циклу, якщо не передбачена умова його завершення. Вони корисні при моделюванні безперервних процесів або генерації необмеженої кількості значень. Модуль `itertools` надає три нескінченні ітератори: `count()`, `cycle()`, і `repeat()`.
#2. Комбінаторні ітератори
Комбінаторні ітератори включають функції для виконання операцій, таких як декартовий добуток, комбінації та перестановки елементів ітератора. Ці інструменти є незамінними при пошуку всіх можливих варіантів впорядкування або об’єднання елементів. Модуль `itertools` надає чотири комбінаторні ітератори: `product()`, `permutations()`, `combinations()` і `combinations_with_replacement()`.
#3. Ітератори, що завершуються на найкоротшій вхідній послідовності
Ці ітератори є кінцевими і застосовуються до кінцевих послідовностей. Вони генерують вихідні дані на основі типу обраної функції. До цієї категорії належать: `accumulate()`, `chain()`, `chain.from_iterable()`, `compress()`, `dropwhile()`, `filterfalse()`, `groupby()`, `islice()`, `pairwise()`, `starmap()`, `takewhile()`, `tee()` та `zip_longest()`.
Розглянемо, як працюють різні функції `itertools` залежно від їхньої категорії:
Нескінченні ітератори
До нескінченних ітераторів належать:
#1. `count()`
Функція `count(start, step)` створює нескінченну послідовність чисел, починаючи з початкового значення `start`. Вона приймає два необов’язкових аргументи: `start` і `step`. Аргумент `start` визначає, з якого числа починається послідовність, і за замовчуванням дорівнює 0, якщо не вказано інше. Аргумент `step` задає різницю між кожним наступним числом. За замовчуванням `step` дорівнює 1.
import itertools # Починаємо відлік з 4, крок 2 for i in itertools.count(4, 2): # Умова для завершення циклу if i == 14: break else: print(i) # Вивід - 4, 6, 8, 10, 12
Вивід:
4 6 8 10 12
#2. `cycle()`
Функція `cycle(iterable)` приймає ітератор як аргумент та повертає ітератор, що нескінченно перебирає елементи заданої послідовності. Коли послідовність вичерпується, цикл починається спочатку.
Наприклад, якщо передати список `[“red”, “green”, “yellow”]` в `cycle()`, то в першому циклі ми отримаємо “red”, в другому – “green”, в третьому – “yellow”. В четвертому циклі знову почнемо з “red” і так далі.
Важливо зберегти результат `cycle()` у змінну, щоб ітератор зберігав свій стан і не починав з початку кожного разу.
import itertools colors = ["red", "green", "yellow"] # Передаємо список кольорів до cycle() color_cycle = itertools.cycle(colors) print(color_cycle) # Використовуємо range для обмеження виводу 7 ітераціями # next() для отримання наступного елемента з ітератора for i in range(7): print(next(color_cycle))
Вивід:
red green yellow red green yellow red
#3. `repeat()`
`repeat(elem, n)` приймає два аргументи: елемент `elem`, який потрібно повторювати, та кількість разів `n`. Елементом може бути як окреме значення, так і ітерований об’єкт. Якщо `n` не вказано, елемент буде повторюватися нескінченно.
import itertools for i in itertools.repeat(10, 3): print(i)
Вивід:
10 10 10
Комбінаторні ітератори
Комбінаторні ітератори включають:
#1. `product()`
`product()` — це функція для обчислення декартового добутку ітераторів. Якщо є дві ітерації, наприклад `x = {7, 8}` та `y = {1, 2, 3}`, їх декартовий добуток складатиметься з усіх можливих комбінацій, де перший елемент з `x`, а другий з `y`. У цьому випадку результат буде `[(7, 1), (7, 2), (7, 3), (8, 1), (8, 2), (8, 3)]`.
`product()` приймає необов’язковий параметр `repeat`, який використовується для обчислення декартового добутку ітератора з самим собою. Значення `repeat` визначає кількість повторень кожного елемента з вхідних ітерацій під час обчислення добутку.
Наприклад, виклик `product(‘ABCD’, repeat=2)` поверне комбінації, як-от `(‘A’, ‘A’)`, `(‘A’, ‘B’)`, `(‘A’, ‘C’)` і так далі. Якщо `repeat` буде 3, то результат матиме вигляд `(‘A’, ‘A’, ‘A’)`, `(‘A’, ‘A’, ‘B’)`, `(‘A’, ‘A’, ‘C’)`, `(‘A’, ‘A’, ‘D’)` і тому подібне.
from itertools import product # product() з необов'язковим аргументом repeat print("product() з необов'язковим аргументом repeat ") print(list(product('ABC', repeat = 2))) # product без repeat print("product() БЕЗ необов'язкового аргументу repeat") print(list(product([7,8], [1,2,3])))
Вивід
product() з необов'язковим аргументом repeat [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')] product() БЕЗ необов'язкового аргументу repeat [(7, 1), (7, 2), (7, 3), (8, 1), (8, 2), (8, 3)]
#2. `permutations()`
`permutations(iterable, group_size)` повертає всі можливі перестановки елементів із заданого ітератора. Перестановка – це спосіб упорядкування елементів. Функція приймає необов’язковий аргумент `group_size`. Якщо `group_size` не вказано, то згенеровані перестановки матимуть таку ж довжину, як і сам ітератор.
import itertools numbers = [1, 2, 3] sized_permutations = list(itertools.permutations(numbers,2)) unsized_permuatations = list(itertools.permutations(numbers)) print("Перестановки з розміром 2") print(sized_permutations) print("Перестановки без аргументу розміру") print(unsized_permuatations)
Вивід
Перестановки з розміром 2 [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)] Перестановки без аргументу розміру [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
#3. `combinations()`
`combinations(iterable, size)` повертає всі можливі комбінації елементів заданого розміру з ітератора. Аргумент `size` визначає розмір кожної комбінації.
Результати впорядковані. Комбінація відрізняється від перестановки тим, що в перестановці важливий порядок, а в комбінації – ні. Наприклад, в `[A, B, C]` є 6 перестановок: AB, AC, BA, BC, CA, CB, але лише 3 комбінації: 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("Комбінації з розміром 2") print(size2_combination) print("Комбінації з розміром 3") print(size3_combination)
Вивід:
Комбінації з розміром 2 [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] Комбінації з розміром 3 [(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
#4. `combinations_with_replacement()`
`combinations_with_replacement(iterable, size)` генерує всі можливі комбінації заданої довжини з ітератора, дозволяючи повторювати елементи у вихідних комбінаціях. `size` визначає розмір згенерованих комбінацій.
Ця функція відрізняється від `combinations()` тим, що повертає комбінації, де елемент може зустрічатися більше одного разу. Наприклад, можна отримати комбінацію, як `(1, 1)`, яку неможливо отримати за допомогою `combinations()`.
import itertools numbers = [1, 2, 3,4] size2_combination = list(itertools.combinations_with_replacement(numbers,2)) print("Combinations_with_replacement => розмір 2") print(size2_combination)
Вивід
Combinations_with_replacement => розмір 2 [(1, 1), (1, 2), (1, 3), (1, 4), (2, 2), (2, 3), (2, 4), (3, 3), (3, 4), (4, 4)]
Кінцеві ітератори
Ці ітератори включають такі функції, як:
#1. `accumulate()`
`accumulate(iterable, function)` приймає ітератор та необов’язковий аргумент – функцію. Він повертає накопичений результат застосування функції до елементів ітератора. Якщо функцію не вказано, то за замовчуванням використовується додавання.
import itertools import operator numbers = [1, 2, 3, 4, 5] # Акумуляція суми чисел accumulated_val = itertools.accumulate(numbers) accumulated_mul = itertools.accumulate(numbers, operator.mul) print("Акумуляція без функції") print(list(accumulated_val)) print("Акумуляція з множенням") print(list(accumulated_mul))
Вивід:
Акумуляція без функції [1, 3, 6, 10, 15] Акумуляція з множенням [1, 2, 6, 24, 120]
#2. `chain()`
`chain(iterable_1, iterable_2, …)` приймає декілька ітераторів та об’єднує їх в один ітератор, що містить значення з усіх переданих ітераторів.
import itertools letters = ['A', 'B', 'C', 'D'] numbers = [1, 2, 3] colors = ['red', 'green', 'yellow'] # Об'єднання букв, чисел і кольорів chained_iterable = list(itertools.chain(letters, numbers, colors)) print(chained_iterable)
Вивід:
['A', 'B', 'C', 'D', 1, 2, 3, 'red', 'green', 'yellow']
#3. `chain.from_iterable()`
`chain.from_iterable(iterable)` схожа на `chain()`, але вона приймає лише один ітератор, який містить підітератори, та об’єднує їх.
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)
Вивід:
['h', 'e', 'l', 'l', 'o', 'red', 'green', 'yellow', 'A', 'B', 'C', 'D', 1, 2, 3]
#4. `compress()`
`compress(data, selectors)` приймає два аргументи: дані (`data`), що є ітератором, і селектори (`selectors`), які також є ітератором, що містить логічні значення `True` і `False`. Також можна використовувати `1` і `0` замість `True` і `False`. `compress()` фільтрує дані, вибираючи лише ті, що відповідають значенням `True` або `1` в `selectors`.
Значення в `data`, які відповідають `True` або `1` у `selectors`, вибираються, а решта ігноруються. Якщо в `selectors` менше логічних значень, ніж елементів в `data`, то елементи `data`, що виходять за межі `selectors`, ігноруються.
import itertools # дані з 10 елементами data = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'] # 9 селекторів selectors = [True, False, 1, False, 0, 1, True, False, 1] # Вибір елементів на основі селекторів filtered_data = list(itertools.compress(data, selectors)) print(filtered_data)
Вивід:
['A', 'C', 'F', 'G', 'I']
#5. `dropwhile()`
`dropwhile(function, sequence)` приймає функцію з умовою, яка повертає `True` або `False`, і послідовність значень. Функція видаляє всі значення з початку послідовності, поки умова повертає `True`. Як тільки умова стає `False`, всі інші елементи послідовності, незалежно від того, задовольняють вони умову чи ні, включаються до результату.
import itertools numbers = [1, 2, 3, 4, 5, 1, 6, 7, 2, 1, 8, 9, 0, 7] # Видаляємо елементи до тих пір, поки умова не буде False filtered_numbers = list(itertools.dropwhile(lambda x: x < 5, numbers)) print(filtered_numbers)
Вивід:
[5, 1, 6, 7, 2, 1, 8, 9, 0, 7]
#6. `filterfalse()`
`filterfalse(function, sequence)` приймає функцію з умовою, що повертає `True` або `False`, та послідовність. Функція повертає значення з послідовності, які не задовольняють умову у функції.
import itertools numbers = [1, 2, 3, 4, 2, 3, 5, 6, 5, 8, 1, 2, 3, 6, 2, 7, 4, 3] # Фільтруємо елементи, для яких умова False filtered_numbers = list(itertools.filterfalse(lambda x: x < 4, numbers)) print(filtered_numbers)
Вивід:
[4, 5, 6, 5, 8, 6, 7, 4]
#7. `groupby()`
`groupby(iterable, key)` приймає ітератор та ключ, а потім створює ітератор, який повертає послідовні ключі та групи. Для правильної роботи ітератор повинен бути відсортований за тією самою ключовою функцією. Ключова функція обчислює значення ключа для кожного елемента ітератора.
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))
Вивід:
Domestic : [('Domestic', 'Cow'), ('Domestic', 'Dog'), ('Domestic', 'Cat')] Wild : [('Wild', 'Lion'), ('Wild', 'Zebra'), ('Wild', 'Elephant')]
#8. `islice()`
`islice(iterable, start, stop, step)` дозволяє розділяти ітератор на основі значень `start`, `stop` і `step`. Аргумент `step` є необов’язковим. Нумерація починається з `0`, і елемент з індексом `stop` не включається до результату.
import itertools numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] # Вибір елементів у діапазоні selected_numbers = list(itertools.islice(numbers, 2, 10)) selected_numbers_step= list(itertools.islice(numbers, 2, 10,2)) print("islice без кроку") print(selected_numbers) print("islice з кроком 2") print(selected_numbers_step)
Вивід:
islice без кроку [3, 4, 5, 6, 7, 8, 9, 10] islice з кроком 2 [3, 5, 7, 9]
#9. `pairwise()`
`pairwise(iterable)` повертає послідовні пари елементів з ітератора в тому порядку, в якому вони з’являються. Якщо ітератор має менше двох елементів, результат буде порожнім.
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)))
Вивід:
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)] [('W', 'O'), ('O', 'R'), ('R', 'L'), ('L', 'D')] []
#10. `starmap()`
`starmap(function, iterable)` – це функція, аналогічна `map()`, але вона використовується, коли параметри аргументів вже згруповані у кортежі. `starmap()` застосовує функцію до елементів ітератора, де кожен елемент ітератора – кортеж.
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)))
Вивід:
[13, 5, 9, 16, 11]
#11. `takewhile()`
`takewhile(function, iterable)` працює навпаки до `dropwhile()`. Функція приймає умову для оцінки та ітератор. Вона включає всі елементи в ітератор, які задовольняють умову, доки умова не стане `False`. Коли умова стає `False`, усі наступні елементи ігноруються.
import itertools numbers = [1, 2, 3, 4, 5, 1, 6, 7, 2, 1, 8, 9, 0, 7] # Вибираємо елементи, доки умова не буде False filtered_numbers = list(itertools.takewhile(lambda x: x < 5, numbers)) print(filtered_numbers)
Вивід:
[1, 2, 3, 4]
#12. `tee()`
`tee(iterable, n)` приймає ітератор і повертає `n` незалежних ітераторів. За замовчуванням `n` дорівнює 2.
import itertools numbers = [1, 2, 3, 4, 5] # Створення двох незалежних ітераторів iter1, iter2 = itertools.tee(numbers, 2) print(list(iter1)) print(list(iter2))
Вивід:
[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
#13. `zip_longest()`
`zip_longest(iterables, fillvalue)` приймає декілька ітераторів та значення `fillvalue`. Функція повертає ітератор, що об’єднує елементи з переданих ітераторів. Якщо ітератори мають різну довжину, відсутні значення замінюються `fillvalue`, поки не буде вичерпано найдовший ітератор.
import itertools names = ['John', 'mathew', 'mary', 'Alice', 'Bob', 'Charlie', 'Fury'] ages = [25, 30, 12, 13, 42] # Об'єднання імен і віку, замінюючи відсутні значення "-" combined = itertools.zip_longest(names, ages, fillvalue="-") for name, age in combined: print(name, age)
Вивід:
John 25 mathew 30 mary 12 Alice 13 Bob 42 Charlie - Fury -
Висновок
Модуль `itertools` є важливим інструментом для будь-якого розробника Python. Функції `itertools` широко використовуються в функціональному програмуванні, при обробці даних, їх фільтрації, групуванні, агрегації, об’єднанні ітерацій, комбінаториці та при роботі з нескінченними послідовностями.
Знання та вміння використовувати `itertools` суттєво покращить продуктивність та якість вашого коду. Не забудьте скористатися цією статтею як посібником у вивченні можливостей цього потужного модуля.