← Comprehensions · 🏠 Домой · Модуль collections →
Коротко. Набор строительных блоков для итераторов, реализованных на C. Все они ленивые — возвращают итератор, а не список, — и заменяют типовые ручные циклы, которые иначе пишутся с ошибками.
Три группы:
| Группа | Функции |
|---|---|
| Бесконечные | count, cycle, repeat |
| Конечные, над входом | chain, islice, groupby, accumulate, zip_longest, tee, batched |
| Комбинаторные | product, permutations, combinations |
Подвох. Ленивость означает одноразовость: результат любой из этих функций
проходится один раз, len() от него не берётся, а печать даёт
<itertools.chain object at ...>, а не содержимое. Чтобы посмотреть — list().
Коротко. chain проходит несколько итерируемых подряд как одно, не создавая
промежуточного списка. islice — срез для итератора, у которого обычный
синтаксис it[a:b] не работает.
from itertools import chain, islice, count
list(chain([1, 2], (3,), "ab"))
# [1, 2, 3, 'a', 'b']
list(chain.from_iterable([[1, 2], [3]])) # когда итерируемые в одном списке
# [1, 2, 3]
list(islice(count(10), 2, 6)) # срез бесконечного счётчика
# [12, 13, 14, 15]chain выигрывает у list1 + list2 на больших данных: конкатенация копирует
всё в новый список, chain не копирует ничего. А islice — штатный способ
взять «первые N» из потока (см. Итератор, где отсутствие
срезов у итераторов отмечено как ограничение).
Подвох. islice не умеет отрицательные индексы: islice(it, -3, None)
даёт ValueError. Иначе пришлось бы дочитать итератор до конца — а он может
быть бесконечным. «Последние N» берут через collections.deque(it, maxlen=N).
Коротко. groupby группирует только соседние элементы с одинаковым
ключом. Он не сортирует вход — это осознанное решение, чтобы работать
потоково и в постоянной памяти.
from itertools import groupby
data = [("a", 1), ("a", 2), ("b", 3), ("a", 4)]
key = lambda t: t[0]
[(k, [v for _, v in g]) for k, g in groupby(data, key=key)]
# [('a', [1, 2]), ('b', [3]), ('a', [4])] — 'a' встретилось дважды
[(k, [v for _, v in g]) for k, g in groupby(sorted(data, key=key), key=key)]
# [('a', [1, 2, 4]), ('b', [3])] — после сортировки как надоПодвох. Вторая ловушка серьёзнее: группа — это итератор, живущий до перехода к следующей группе. Сохранить группы «на потом» нельзя:
saved = list(groupby([1, 1, 2]))
[(k, list(g)) for k, g in saved]
# [(1, []), (2, [])] — группы уже исчерпаныМатериализовать группу нужно сразу — list(g) внутри цикла.
Глубже. Когда группировать надо по всей последовательности, а сортировка
дорога, defaultdict(list) в один проход и дешевле, и понятнее
(см. Модуль collections). groupby уместен там, где
данные уже упорядочены: строки отсортированного файла, ответ базы с
ORDER BY, поток событий по времени.
Коротко. product — декартово произведение (вложенные циклы),
permutations — размещения (порядок важен), combinations — сочетания
(порядок не важен, только «вперёд» по входу).
from itertools import product, permutations, combinations
list(product([1, 2], "ab"))
# [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
list(permutations("abc", 2))
# [('a','b'), ('a','c'), ('b','a'), ('b','c'), ('c','a'), ('c','b')]
list(combinations("abc", 2))
# [('a','b'), ('a','c'), ('b','c')]product(xs, repeat=3) заменяет три вложенных цикла по одному и тому же
набору — типовой перебор в задачах на собеседовании.
Подвох. Уникальность элементов не проверяется: permutations работает по
позициям, поэтому на входе "aab" появятся визуально одинаковые кортежи.
И размер результата растёт факториально — материализовать его в список
без ограничения по islice опасно.
-
accumulate(iterable, func=operator.add)— running total ([1,2,3,4]→[1, 3, 6, 10]); сfunc=maxдаёт бегущий максимум. -
zip_longest(*iterables, fillvalue=...)—zipостанавливается по короткому входу и молча теряет хвост;zip_longestдополняет заполнителем. Третий вариант —zip(..., strict=True)(Python 3.10+), который на разной длине бросаетValueError. -
batched(iterable, n)(Python 3.12+) — нарезка потока на пачки: то, что раньше писали руками для батчевых вставок в базу.# python 3.12+ from itertools import batched list(batched("abcdefg", 3)) # [('a','b','c'), ('d','e','f'), ('g',)]
-
tee(iterable, n)— «раздвоить» итератор. Помнить о цене:teeбуферизует элементы, которые уже прочитала одна ветка и ещё не прочитала другая. Если ветки расходятся далеко, в памяти окажется весь вход, и проще сделатьlist().