В Python легко разбить список длиной n на фрагменты размером k, если n кратно k< /em> (IOW, n % k == 0
). Вот мой любимый подход (прямо из документы ):
>>> 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)]
Есть ли простой способ изменить идиому map(None, *[iter(x)]*k)
для получения такого результата?
(Конечно, эту проблему несложно решить, написав функцию (см., например, множество прекрасных ответов на Как разбить список на куски одинакового размера? или Какой самый питонический способ перебора списка по частям?). Следовательно, более точным названием для этого вопроса было бы «Как спасти идиому map(None, *[iter(x)]*k)
?», но я думаю, что это сбило бы многих читателей с толку.)
Меня поразило, как легко разбить список на куски одинакового размера и как сложно (в сравнении!) избавиться от ненужного заполнения, хотя эти две проблемы кажутся сопоставимая сложность.