Напишите функцию chunked(iterable, size), которая разбивает любой итерируемый объект на части по size элементов.
Короткий ответ
- Решение — генератор: лениво отдаёт списки по size элементов
- Должно работать с любым iterable, не только списком
- iter() + islice накапливают очередную порцию
- Последняя порция может быть короче size
- Нельзя использовать срезы — у генераторов их нет
- В 3.12+ есть готовый itertools.batched
Корректное решение берёт итератор от входа и лениво отдаёт порции через islice, работая с любыми iterable и неполной последней порцией.
Как сказать вслух
пример ответаВажно, что на вход может прийти не список, а любой итерируемый объект — генератор или файл, у которых нет срезов и длины. Поэтому я беру от входа итератор и в цикле набираю из него порцию через islice: получилась пустая — выходим, иначе отдаю её через yield. Так функция работает лениво и с бесконечными источниками. В свежих версиях Python есть готовый itertools.batched, и я бы об этом сказал.
Подробный ответ
Основной ответ
Наивное решение через срезы lst[i:i+size] работает только с последовательностями: у генератора, файла или сокет-стрима нет ни len(), ни срезов. Универсальный подход: получить единый итератор it = iter(iterable) и в бесконечном цикле собирать chunk = list(islice(it, size)); пустой chunk означает исчерпание — return, иначе yield chunk. Ключевой нюанс — iter() вызывается один раз: islice каждый раз продолжает с места остановки того же итератора. Решение ленивое (порция создаётся по запросу), память O(size), подходит для бесконечных потоков. Стоит валидировать size >= 1 (иначе вечный цикл) и знать стандартные аналоги: itertools.batched (Python 3.12+, отдаёт кортежи) и more_itertools.chunked.
Ключевые моменты
- Один итератор. iter() до цикла — обязательное условие: каждый islice продолжает чтение, а не начинает сначала.
- Ленивость. Генератор не материализует весь вход, поэтому справится с файлом на гигабайты и бесконечным потоком.
- Краевые случаи. Пустой вход — ноль порций; длина не кратна size — короткий хвост; size <= 0 — ValueError.
- Стандартная библиотека. itertools.batched в 3.12+ решает задачу из коробки; упоминание показывает актуальность знаний.
Практический контекст
Задача проверяет сразу протокол итераторов, генераторы и внимание к краевым случаям — поэтому её любят на секциях по Python. В работе паттерн встречается постоянно: пакетная вставка в БД по 1000 строк, отправка сообщений в API с лимитом на размер батча, обработка больших файлов.
Пример кода
from itertools import islice
def chunked(iterable, size):
if size < 1:
raise ValueError("size must be >= 1")
it = iter(iterable)
while True:
chunk = list(islice(it, size))
if not chunk:
return
yield chunk
print(list(chunked(range(7), 3))) # [[0,1,2], [3,4,5], [6]]
print(list(chunked((x for x in "abcd"), 2))) # [['a','b'], ['c','d']]Частые ошибки
- Решают через срезы и len(), ломаясь на генераторах
- Вызывают iter() внутри цикла и бесконечно читают одну и ту же первую порцию
- Не обрабатывают size <= 0 и получают бесконечный цикл пустых порций