Простая идиома, чтобы разбить список длиной n на блоки длиной k, когда n% k> 0?

В Python легко разбить n -длинный список на блоки размером k , если n кратное k (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)]

Есть ли простой способ изменить карту (None, * [iter (x)] * k) идиома, чтобы получить такой результат?

(Конечно, решить несложно решить эту проблему, написав функцию (см., например, множество прекрасных ответов на Как разделить список на части одинакового размера? или Какой самый «питонический» способ перебора список в кусках? ). Поэтому более точным названием для этого вопроса было бы «Как спасти карту (None, * [iter (x)] * k) идиома?», но Думаю, это сбило бы с толку многих читателей.)

Меня поразило, насколько легко разбить список на части равного размера и насколько сложно ( для сравнения! ) его получить избавиться от нежелательного заполнения, даже если две проблемы кажутся сопоставимая сложность.

12
задан Community 23 May 2017 в 10:33
поделиться