3.4. Встроенные возможности по работе с коллекциями

В этой статье вы познакомитесь с расширенными возможностями Python для работы с коллекциями. Вы научитесь использовать библиотеку itertools, чтобы эффективно обрабатывать и комбинировать данные, даже если они приходят из разных источников. Разберётесь, как создавать бесконечные итераторы, объединять и фильтровать коллекции, а также применять функции enumerate() и zip() в практических задачах. Эти инструменты помогут писать более компактный и читаемый код.

Ключевые вопросы статьи

  • Что такое библиотека itertools и зачем она нужна?
  • Какие типы итераторов можно создавать с помощью функций count(), cycle() и repeat()?
  • Чем функции accumulate(), chain() и product() удобны при работе с коллекциями?
  • Как устроены комбинаторные функции permutations() и combinations()?
  • Когда полезно использовать enumerate() и zip() и чем они различаются?

Что такое библиотека itertools и зачем она нужна

В Python множество встроенных инструментов для работы с коллекциями — методы, функции, конструкции языка. Но иногда нужно выполнить более сложные или нестандартные операции: перебрать все комбинации элементов, объединить несколько коллекций, повторять значение в цикле и т. п. В таких случаях на помощь приходят библиотеки — модули с готовыми функциями.

В этой статье мы работаем с itertools — стандартной библиотекой Python, созданной специально для обработки коллекций. Её вдохновляли функциональные языки вроде APL и Haskell. С помощью itertools можно строить итераторы, которые:

  • экономят память (не создают список целиком);
  • позволяют легко комбинировать и фильтровать данные;
  • ускоряют работу с коллекциями в циклах.

Чтобы использовать библиотеку, её нужно импортировать:

import itertools

print(itertools.product("ABC", repeat=2))

Если хотите, чтобы функции библиотеки вызывались без префикса itertools., используйте такой синтаксис:

from itertools import product
print(product("ABC", repeat=2))

Важно

Почти все функции itertools возвращают итераторы, а не списки. Их можно обойти в цикле или преобразовать в список с помощью list().

Какие типы итераторов можно создавать с помощью функций count(), cycle() и repeat()

Некоторые задачи требуют многократного перебора значений — бесконечно или по заданному шаблону. Вместо того чтобы вручную писать циклы, можно использовать готовые итераторы из itertools. Они работают «лениво»: не создают коллекции целиком, а выдают элементы по мере запроса — удобно и эффективно.

count(): счётчик с шагом

Создаёт бесконечную числовую последовательность. Можно задать старт и шаг. Работает с целыми и вещественными числами.

from itertools import count

for value in count(0, 0.1):
    if value > 1:
        break
    print(round(value, 1))
# Вывод:
# 0, 0.1, 0.2, ... 1.0

cycle(): зацикливание коллекции

Бесконечно перебирает элементы переданной коллекции. Полезно, когда нужно «крутить» одно и то же в цикле — например, при визуализации анимации, смене состояний или поочерёдном присвоении меток.

from itertools import cycle

s = ""
for letter in cycle("ABC"):
    if len(s) == 10:
        break
    s += letter

print(s)

# Вывод:
# ABCABCABCA

repeat(): повтор одного значения

Повторяет одно и то же значение. Можно указать, сколько раз.

from itertools import repeat
print(list(repeat("ABC", 5)))

# Вывод:
# ['ABC', 'ABC', 'ABC', 'ABC', 'ABC']

Чем функции accumulate(), chain() и product() удобны при работе с коллекциями

Некоторые задачи требуют не просто перебрать элементы, а накапливать значения, объединять разные коллекции или перебрать все возможные комбинации. Для этого в itertools есть специальные функции, которые делают такие операции лаконичными и эффективными.

accumulate(): накопление значений

Создаёт итератор, в котором каждое значение — это сумма всех предыдущих. Полезно, если нужно посчитать прогресс, баллы или накопленные значения в процессе.

from itertools import accumulate

for value in accumulate([1, 2, 3, 4, 5]):
    print(value)

# Вывод:
# 1, 3, 6, 10, 15

chain() и chain.from_iterable(): объединение коллекций

Позволяют объединить несколько итерируемых объектов в один. Это удобнее и быстрее, чем склеивать списки вручную.

from itertools import chain

values = list(chain("АБВ", "ГДЕ", "ЖЗИ"))
print(values)

# Вывод программы:
# ['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З', 'И']

Если у вас список строк, можно использовать chain.from_iterable():

from itertools import chain

values = list(chain.from_iterable(["АБВ", "ГДЕ", "ЖЗИ"]))

print(values)

Результат будет тот же.

product(): декартово произведение

Функция product() из модуля itertools создаёт все возможные упорядоченные комбинации элементов из нескольких коллекций. Это называется декартовым произведением.

Полезна при генерации вариантов:

  • комбинаций настроек,
  • паролей,
  • расписаний,
  • всех пар ключей и значений и др.
Пример 1. Простое произведение двух коллекций
from itertools import product

values = list(product([1, 2, 3], "АБВГ"))
print(values)

# Вывод программы:
# [(1, 'А'), 
# (1, 'Б'), 
# (1, 'В'),
# (1, 'Г'),
# (2, 'А'),
# (2, 'Б'),
# (2, 'В'),
# (2, 'Г'),
# (3, 'А'),
# (3, 'Б'),
# (3, 'В'),
# (3, 'Г')]
Пример 2. Повторные сочетания (repeat=2)

Получаем все возможные четвёрки вида (число, буква, число, буква). Аргумент repeat=2 означает, что каждая коллекция участвует дважды и мы строим комбинации длины 4.

from itertools import product
values = list(product([1, 2, 3], "АБВГ", repeat=2))

print(values)

Это аналогично:

product([1, 2, 3], "АБВГ", [1, 2, 3], "АБВГ")

# Вывод программы:
# [(1, 'А', 1, 'А'), 
# (1, 'А', 1, 'Б'), 
# (1, 'А', 1, 'В'), 
# ..., 
# (3, 'Г', 3, 'А'),
# (3, 'Г', 3, 'Б'),
# (3, 'Г', 3, 'В'), 
# (3, 'Г', 3, 'Г')]

Как устроены комбинаторные функции permutations() и combinations()

Когда нужно перебрать все возможные сочетания или перестановки элементов, ручное решение может оказаться громоздким. Модуль itertools предлагает три удобные функции комбинаторики: permutations(), combinations() и combinations_with_replacement().

permutations(): перестановки без повторений

Создаёт все возможные упорядоченные варианты из элементов. По умолчанию длина каждой перестановки равна длине исходного объекта, но можно указать другую длину через аргумент r.

from itertools import permutations

values = list(permutations("АБВ"))
print(values)

# Вывод:
# [('А', 'Б', 'В'),
# ('А', 'В', 'Б'),
# ('Б', 'А', 'В'),
# ('Б', 'В', 'А'),
# ('В', 'А', 'Б'),
# ('В', 'Б', 'А')]

combinations(): сочетания без повторений

Создаёт неупорядоченные комбинации заданной длины r. Элементы внутри кортежей идут по порядку, но не повторяются. Пары вроде ('А', 'Б') и ('Б', 'А') считаются одинаковыми, и в результате будет только одна из них.

from itertools import combinations

values = list(combinations("АБВ", 2))
print(values)

# Вывод:
# [('А', 'Б'), ('А', 'В'), ('Б', 'В')]

combinations_with_replacement(): сочетания с повторениями

Похож на combinations(), но допускает повторы. То есть элементы могут встречаться в одном сочетании больше одного раза.

from itertools import combinations_with_replacement

values = list(combinations_with_replacement("АБВ", 2))
print(values)

# Вывод:
# [('А', 'А'), 
# ('А', 'Б'),
# ('А', 'В'), 
# ('Б', 'Б'),
# ('Б', 'В'),
# ('В', 'В')]

Когда полезно использовать enumerate() и zip() и чем они различаются

В Python есть два полезных инструмента, которые часто применяются в циклах: enumerate() и zip(). Они делают код более компактным и читаемым, особенно при работе с коллекциями.

enumerate(): индексация элементов

Если нужно получить одновременно и индекс, и значение из коллекции — используйте enumerate(). По умолчанию индексация начинается с 0, но можно задать другой старт.

for index, value in enumerate("ABC", 1):
    print(index, value)

# Вывод:
# 1 A  
# 2 B  
# 3 C

Полезно, когда вы перебираете элементы и при этом хотите показывать порядковый номер или использовать его в логике программы.

zip(): объединение коллекций по индексам

Функция zip() соединяет несколько коллекций, создавая кортежи из элементов с одинаковыми индексами. Это удобно, например, при работе с парами «имя — значение».

print(list(zip("ABC", [1, 2, 3])))

# Вывод:
# [('A', 1), ('B', 2), ('C', 3)]

Если длины коллекций разные, zip() остановится на самой короткой:

print(list(zip("ABCDE", [1, 2, 3])))

# Вывод:
# [('A', 1), ('B', 2), ('C', 3)]

Чтобы избежать неожиданных сокращений, можно включить проверку:

print(list(zip("ABCDE", [1, 2, 3], strict=True)))

# Вывод:
# ValueError: zip() argument 2 is shorter than argument 1

Различие между ними:

  • enumerate() работает с одной коллекцией и добавляет к каждому элементу его индекс;
  • zip() работает с несколькими коллекциями и объединяет элементы с одинаковыми индексами в кортежи.

Обе функции делают код лаконичнее — особенно в циклах, генераторах и при обработке табличных данных.

Ещё по теме

Более подробно обо всех функциях модуля itertools можно почитать в документации.

Что дальше

Теперь вы умеете использовать встроенные и внешние инструменты Python для работы с коллекциями: создавать бесконечные итераторы (count, cycle, repeat), объединять и фильтровать данные (chain, accumulate), перебирать все комбинации (product, permutations, combinations), а также применять enumerate() и zip() для удобной обработки в циклах.

Следующий шаг — научиться работать с файлами и потоками ввода-вывода. Мы разберём, как считывать данные и сохранять результаты в текстовые файлы с помощью open(), read(), write() и with, а также научимся загружать и записывать структурированные данные в формате JSON — чтобы удобно хранить информацию и обмениваться ей между программами.

Ключевые выводы статьи

  • Библиотека itertools — мощный инструмент для работы с коллекциями: она содержит готовые итераторы, которые помогают писать компактный и эффективный код.
  • Функции count(), cycle() и repeat() позволяют создавать бесконечные последовательности и удобно использовать их в циклах.
  • С помощью accumulate(), chain() и product() можно накапливать значения, объединять коллекции и перебирать все возможные комбинации.
  • Функции permutations(), combinations() и combinations_with_replacement() полезны для генерации всех вариантов выборок из коллекции.
  • enumerate() добавляет к элементам коллекции их порядковые номера, а zip() объединяет несколько коллекций по индексам — это делает обработку данных удобной и читаемой.