Использование модуля itertools в Python

Использование модуля itertools в Python

В данном руководстве мы рассмотрим использование основных методов count(), cycle() и chain() стандартной библиотеки itertools в Python.

Введение

В Python есть множество встроенных инструментов, которые позволяют нам выполнять итерации и преобразовывать данные. Отличным примером является модуль itertools, который предлагает несколько удобных функций итерации. Каждая из этих функций построения итераторов (они генерируют итераторы) может использоваться самостоятельно или комбинироваться.

Модуль был вдохновлен функциональными языками, такими как APL, Haskell и SPL, а элементы внутри itertools образуют алгебру итераторов Python.

Итератор и итерабельность

Прежде чем мы погрузимся в итерацию, давайте сначала определим различие между двумя важными терминами: iterable и iterator.

Итерабельность — это объект, по которому можно выполнять итерации. При использовании функции iter() создается итератор. Вообще говоря, большинство последовательностей являются итерируемыми, например, списки, кортежи, строки и т.д.

Итератор — это также объект, который используется для итерации по итерируемой последовательности. Итератор также может итерировать сам себя. Это делается с помощью метода next(), передавая итератор, который мы пытаемся обойти.

Метод next() возвращает следующий элемент объекта итератора. Итератор может быть сгенерирован из итерабельного объекта (с помощью функции iter()):

word_list = ["a", "b", "c", "d", "f"]
iterator = iter(word_list)

print(iterator)

Вывод программы

<list_iterator object at 0x7fa6220edf10>

Теперь давайте обратимся к элементу next() (начиная с первого), используя наш итератор:

print(next(iterator))

Вывод программы

a

Это практически то, что происходит под капотом цикла for — он вызывает функцию iter() на коллекции, над которой вы итерируетесь, и после этого к элементу next() обращаются n раз.

В этой статье мы рассмотрим несколько инструментов итерации в Python:

  • count()
  • cycle()
  • chain()

Функция count()

Функция count(start, step) создает итератор и используется для генерации равномерно расположенных значений, где промежуток между ними определяется аргументом step. Аргумент start определяет начальное значение итератора — по умолчанию это значение равно start=0 и step=1.

Без условия прерывания функция count() будет продолжать считать бесконечно (в системе с неограниченной памятью):

from itertools import count

iter_count = count(start=0, step=10)

for i_c in iter_count:
    if i_c == 100:
        break
    print(i_c)

Примечание: Подобное использование count() необычно. Обычно ее используют в цепочке с другими методами, такими как zip(), map() или imap().

Здесь итератор итерирует сам себя, печатая значения с шагом 10:

0
10
20
30
40
50
60
70
80
90

Учитывая ее генеративный характер, эта функция чаще всего используется вместе с другими функциями, которые ожидают новых или генерирующих последовательностей.

Например, при использовании функции zip() для сшивания нескольких элементов списка, вы можете захотеть аннотировать их с помощью позиционного индекса. В процессе выполнения функции zip() мы будем использовать функцию count() для генерации значений для этих индексов:

from itertools import count

example_list = ["Andrey", "Anton", "Dmitriy", "Alexandr"]
for i in zip(count(), example_list):
    print(i)

Вывод программы

(0, 'Andrey')
(1, 'Anton')
(2, 'Dmitriy')
(3, 'Alexandr')

Функция cycle()

Функция cycle() принимает итератор и генерирует итератор, который содержит все элементы итератора. Помимо этих элементов, он содержит копию каждого элемента.

Как только мы дойдем до конца элементов, мы начнем итерацию по копиям. Во время перебора копий создаются новые копии. Как только первый набор копий заканчивается, мы начинаем итерацию по новому набору.

Этот процесс повторяется бесконечно.

Примечание: Учитывая этот факт, использование функции cycle(), особенно для длинных последовательностей, отнимает много памяти. Остерегайтесь бесконечной, рекурсивной логики создания, так как у вас легко закончится память для размещения всего этого:

from itertools import cycle

example_list = ["a", "b", "c", "d", "f"]
iterator = cycle(example_list)

for i in iterator:
    print(i)

Вывод программы

a
b
c
d
f
a
b
c
d
...

Пока мы не завершим программу или не закончится память. Учитывая это, необходимо всегда иметь условие выхода/завершения для функции cycle().

Учитывая тот факт, что функция cycle() может перебирать любые итерации, мы можем легко применить ее к строкам и кортежам.

Функция chain()

Функция chain() используется для объединения в цепочку нескольких итераторов, создавая итератор, который обходит их последовательно, один за другим:

from itertools import chain

example_list_one = [1, 2, 3, 4, 5]
example_list_two = ["Egor", "Denis", "Oleg", "Anton", "George"]
example_string_one = "Test1"
example_string_two = "Test2"

result = list(
    chain(example_list_one, example_list_two, example_string_one, example_string_two)
)

print(result)

Вывод программы

[1, 2, 3, 4, 5, 'Egor', 'Denis', 'Oleg', 'Anton', 'George', 'T', 'e', 's', 't', '1', 'T', 'e', 's', 't', '2']

Здесь у нас есть четыре различных типа итерабельных элементов, каждый из которых соединяется в цепочку.

Несмотря на то, что [«Egor», «Denis», «Oleg», «Anton», «George»] — это список строк, chain() рассматривает его как список и просто соединяет его элементы в цепочку без вызова последующей chain() для каждой из строк. С другой стороны, «Test1» разбивается на составляющие ее символы.

Первое может быть достигнуто с помощью другого метода, производного от функции chain() — chain.from_iterable():

from itertools import chain

example_list = ["Egor", "Denis", "Oleg", "Anton", "George"]

print(list(chain(example_list)))
print(list(chain.from_iterable(example_list)))

Функция chain() ведет себя так же, как мы наблюдали ранее — она выстраивает элементы в цепочку, как они есть. С другой стороны, метод chain.from_iterable() рассматривает каждый элемент как итерабельность и возвращает его составные элементы вместе с другими элементами, разбитыми таким же образом:

['Egor', 'Denis', 'Oleg', 'Anton', 'George']
['E', 'g', 'o', 'r', 'D', 'e', 'n', 'i', 's', 'O', 'l', 'e', 'g', 'A', 'n', 't', 'o', 'n', 'G', 'e', 'o', 'r', 'g', 'e']

Обычно вы используете chain.from_iterable() для вычисления суммы цифр, содержащихся в нескольких коллекциях, которые вы сначала объединяете в цепочку, а затем вычисляете для них sum():

from itertools import chain

example_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
result = list(chain.from_iterable(example_list))
print(sum(result))

Каждый элемент коллекции example_list — это еще один список. Поскольку списки итерируемы, вызов chain.from_iterable() разбивает их на один список, содержащий элементы из [1..9], после чего мы вычисляем их sum() и выводим результат:

45

Заключение

Модуль itertools знакомит нас с несколькими полезными удобными функциями для работы с итерациями и итерациями.

Многие из них можно использовать как самостоятельные функции, но чаще всего они используются в связке с другими функциями для преобразования данных.

Егор Егоров

Программирую на Python с 2017 года. Люблю создавать контент, который помогает людям понять сложные вещи. Не представляю жизнь без непрерывного цикла обучения, спорта и чувства юмора.

Ссылка на мой github есть в шапке. Залетай.

Оцените автора
Егоров Егор
Добавить комментарий