В Python легко разбить n-длинный список на k-размерные куски, если n кратно k (IOW, n % k == 0
). Здесь мой любимый подход (прямо из docs):
>>> k = 3
>>> n = 5 * k
>>> x = range(k * 5)
>>> zip(*[iter(x)] * k)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]
(Фокус в том, что [iter(x)] * k
создает список k ссылок на один и тот же итератор, возвращаемый iter(x)
. Затем zip
генерирует каждый фрагмент, вызывая каждую из k копий итератора ровно один раз. *
до [iter(x)] * k
необходимо, потому что zip
ожидает получить свои аргументы как "отдельные" итераторы, а не их список.)
Основной недостаток, который я вижу в этой идиоме, состоит в том, что, когда n не кратно k (IOW, n % k > 0
), левые записи просто оставлены; например:.
>>> zip(*[iter(x)] * (k + 1))
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
Там альтернативная идиома, которая немного длиннее для ввода, дает тот же результат, что и выше, когда n % k == 0
, и имеет более приемлемое поведение, когда n % k > 0
:
>>> map(None, *[iter(x)] * k)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]
>>> map(None, *[iter(x)] * (k + 1))
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14, None)]
По крайней мере, здесь оставшиеся записи сохраняются, но последний кусок добавляется с помощью None
. Если для заполнения требуется просто другое значение, то itertools.izip_longest
решает проблему.
Но предположим, что желаемое решение - это тот, в котором последний кусок оставлен без запаса, т.е.
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14)]
Есть ли простой способ изменить idiom map(None, *[iter(x)]*k)
для создания этого результата?
(Конечно, нетрудно решить эту проблему, написав функцию (см., например, множество мелких ответов на . Как вы разбиваете список на куски с равномерным размером? или Каков самый "питонический" способ перебора списка в кусках?. Поэтому более точное название для этого вопроса будет "Как избавиться от map(None, *[iter(x)]*k)
идиомы?", Но я думаю, что это сбивало бы с толку многих читателей.)
Я был поражен тем, насколько легко разбивать список на четные куски и насколько сложно (в сравнении!) это избавиться от нежелательной прокладки, хотя обе проблемы кажутся сопоставимой сложности.