У меня есть список произвольной длины, и мне нужно разделить его на куски одинакового размера и обработать его. Есть несколько очевидных способов сделать это, например, сохранить счетчик и два списка, и, когда второй список заполнится, добавить его в первый список и очистить второй список для следующего раунда данных, но это потенциально чрезвычайно дорого.
Мне было интересно, есть ли у кого-нибудь хорошее решение для списков любой длины, например, используя генераторы.
Я искал что-то полезное в itertools
, но не смог найти что-то полезное. Возможно, я пропустил это.
Смежный вопрос: Что такое самый "питонический" способ перебирать список кусками?
31 ответ
Вот генератор, который дает нужные вам куски:
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i:i + n]
import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
Если вы используете Python 2, вы должны использовать xrange()
вместо range()
:
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in xrange(0, len(lst), n):
yield lst[i:i + n]
Также вы можете просто использовать списочное понимание вместо написания функции, хотя было бы неплохо инкапсулировать подобные операции в именованные функции, чтобы ваш код было легче понять. Python 3:
[lst[i:i + n] for i in range(0, len(lst), n)]
Версия Python 2:
[lst[i:i + n] for i in xrange(0, len(lst), n)]
def split_seq(seq, num_pieces):
start = 0
for i in xrange(num_pieces):
stop = start + len(seq[i::num_pieces])
yield seq[start:stop]
start = stop
Использование:
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for seq in split_seq(seq, 3):
print seq
Критика других ответов здесь:
Ни один из этих ответов не является кусками одинакового размера, все они оставляют в конце грубый кусок, поэтому они не полностью сбалансированы. Если бы вы использовали эти функции для распределения работы, вы встроили перспективу того, что одна из них, вероятно, закончится задолго до других, поэтому она будет бездействовать, пока другие будут продолжать усердно работать.
Например, текущий верхний ответ заканчивается:
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
Я просто ненавижу этот ворчать в конце!
Другие, такие как list(grouper(3, xrange(7)))
и chunk(xrange(7), 3)
оба возвращают: [(0, 1, 2), (3, 4, 5), (6, None, None)]
. None
- это просто отступы, и, на мой взгляд, они довольно не элегантны. Они НЕ равномерно разбивают фрагменты.
Почему мы не можем разделить это лучше?
Мое решение (я)
Вот сбалансированное решение, адаптированное из функции, которую я использовал в производстве (обратите внимание на Python 3, чтобы заменить xrange
на range
):
def baskets_from(items, maxbaskets=25):
baskets = [[] for _ in xrange(maxbaskets)] # in Python 3 use range
for i, item in enumerate(items):
baskets[i % maxbaskets].append(item)
return filter(None, baskets)
И я создал генератор, который делает то же самое, если вы поместите его в список:
def iter_baskets_from(items, maxbaskets=3):
'''generates evenly balanced baskets from indexable iterable'''
item_count = len(items)
baskets = min(item_count, maxbaskets)
for x_i in xrange(baskets):
yield [items[y_i] for y_i in xrange(x_i, item_count, baskets)]
И наконец, поскольку я вижу, что все вышеперечисленные функции возвращают элементы в непрерывном порядке (как они были заданы):
def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
'''
generates balanced baskets from iterable, contiguous contents
provide item_count if providing a iterator that doesn't support len()
'''
item_count = item_count or len(items)
baskets = min(item_count, maxbaskets)
items = iter(items)
floor = item_count // baskets
ceiling = floor + 1
stepdown = item_count % baskets
for x_i in xrange(baskets):
length = ceiling if x_i < stepdown else floor
yield [items.next() for _ in xrange(length)]
Выход
Чтобы проверить их:
print(baskets_from(xrange(6), 8))
print(list(iter_baskets_from(xrange(6), 8)))
print(list(iter_baskets_contiguous(xrange(6), 8)))
print(baskets_from(xrange(22), 8))
print(list(iter_baskets_from(xrange(22), 8)))
print(list(iter_baskets_contiguous(xrange(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(xrange(26), 5))
print(list(iter_baskets_from(xrange(26), 5)))
print(list(iter_baskets_contiguous(xrange(26), 5)))
Который распечатывает:
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]]
Обратите внимание, что непрерывный генератор обеспечивает фрагменты в тех же шаблонах длины, что и два других, но все элементы расположены по порядку, и они разделены настолько равномерно, что можно разделить список дискретных элементов.
[AA[i:i+SS] for i in range(len(AA))[::SS]]
Где AA - массив, SS - размер чанка. Например:
>>> AA=range(10,21);SS=3
>>> [AA[i:i+SS] for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
# or [range(10, 13), range(13, 16), range(16, 19), range(19, 21)] in py3
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
CHUNK = 4
[a[i*CHUNK:(i+1)*CHUNK] for i in xrange((len(a) + CHUNK - 1) / CHUNK )]
Просмотрите эту ссылку.
>>> orange = range(1, 1001)
>>> otuples = list( zip(*[iter(orange)]*10))
>>> print(otuples)
[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), ... (991, 992, 993, 994, 995, 996, 997, 998, 999, 1000)]
>>> olist = [list(i) for i in otuples]
>>> print(olist)
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]
>>>
Python3
Библиотека toolz имеет функцию partition
для этого:
from toolz.itertoolz.core import partition
list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]
Мне было интересно узнать о производительности различных подходов, и вот оно:
Проверено на Python 3.5.1
import time
batch_size = 7
arr_len = 298937
#---------slice-------------
print("\r\nslice")
start = time.time()
arr = [i for i in range(0, arr_len)]
while True:
if not arr:
break
tmp = arr[0:batch_size]
arr = arr[batch_size:-1]
print(time.time() - start)
#-----------index-----------
print("\r\nindex")
arr = [i for i in range(0, arr_len)]
start = time.time()
for i in range(0, round(len(arr) / batch_size + 1)):
tmp = arr[batch_size * i : batch_size * (i + 1)]
print(time.time() - start)
#----------batches 1------------
def batch(iterable, n=1):
l = len(iterable)
for ndx in range(0, l, n):
yield iterable[ndx:min(ndx + n, l)]
print("\r\nbatches 1")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
tmp = x
print(time.time() - start)
#----------batches 2------------
from itertools import islice, chain
def batch(iterable, size):
sourceiter = iter(iterable)
while True:
batchiter = islice(sourceiter, size)
yield chain([next(batchiter)], batchiter)
print("\r\nbatches 2")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
tmp = x
print(time.time() - start)
#---------chunks-------------
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
print("\r\nchunks")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in chunks(arr, batch_size):
tmp = x
print(time.time() - start)
#-----------grouper-----------
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)
def grouper(iterable, n, padvalue=None):
"grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)
arr = [i for i in range(0, arr_len)]
print("\r\ngrouper")
start = time.time()
for x in grouper(arr, batch_size):
tmp = x
print(time.time() - start)
< Сильный > Результаты :
slice
31.18285083770752
index
0.02184295654296875
batches 1
0.03503894805908203
batches 2
0.22681021690368652
chunks
0.019841909408569336
grouper
0.006506919860839844
Например, если у вас размер куска 3, вы можете сделать:
zip(*[iterable[i::3] for i in range(3)])
Источник: http://code.activestate.com/recipes/303060-group -a - список - в - последовательного - н- кортежи /
Я бы использовал это, когда мой размер фрагмента является фиксированным числом, которое я могу напечатать, например. «3», и никогда не изменится.
Хех, однострочная версия
In [48]: chunk = lambda ulist, step: map(lambda i: ulist[i:i+step], xrange(0, len(ulist), step))
In [49]: chunk(range(1,100), 10)
Out[49]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
[31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
[51, 52, 53, 54, 55, 56, 57, 58, 59, 60],
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70],
[71, 72, 73, 74, 75, 76, 77, 78, 79, 80],
[81, 82, 83, 84, 85, 86, 87, 88, 89, 90],
[91, 92, 93, 94, 95, 96, 97, 98, 99]]
На данный момент, я думаю, нам нужен рекурсивный генератор , на всякий случай ...
В питоне 2:
def chunks(li, n):
if li == []:
return
yield li[:n]
for e in chunks(li[n:], n):
yield e
В питоне 3:
def chunks(li, n):
if li == []:
return
yield li[:n]
yield from chunks(li[n:], n)
Кроме того, в случае массового вторжения инопланетян может пригодиться декорированный рекурсивный генератор :
def dec(gen):
def new_gen(li, n):
for e in gen(li, n):
if e == []:
return
yield e
return new_gen
@dec
def chunks(li, n):
yield li[:n]
for e in chunks(li[n:], n):
yield e
def chunk(input, size):
return map(None, *([iter(input)] * size))
Я знаю, что это старое, но никто еще не упомянул {{X0} } :
import numpy as np
lst = range(50)
np.array_split(lst, 5)
# [array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
# array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
# array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
# array([30, 31, 32, 33, 34, 35, 36, 37, 38, 39]),
# array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])]
Попробуйте использовать matplotlib.cbook
Например:
import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
print s
def chunks(iterable,n):
"""assumes n is an integer>0
"""
iterable=iter(iterable)
while True:
result=[]
for i in range(n):
try:
a=next(iterable)
except StopIteration:
break
else:
result.append(a)
if result:
yield result
else:
break
g1=(i*i for i in range(10))
g2=chunks(g1,3)
print g2
'<generator object chunks at 0x0337B9B8>'
print list(g2)
'[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81]]'
Без вызова len (), что хорошо для больших списков:
def splitter(l, n):
i = 0
chunk = l[:n]
while chunk:
yield chunk
i += n
chunk = l[i:i+n]
И это для повторяемости:
def isplitter(l, n):
l = iter(l)
chunk = list(islice(l, n))
while chunk:
yield chunk
chunk = list(islice(l, n))
Функциональный аромат вышеперечисленного:
def isplitter2(l, n):
return takewhile(bool,
(tuple(islice(start, n))
for start in repeat(iter(l))))
ИЛИ ЖЕ:
def chunks_gen_sentinel(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return iter(imap(tuple, continuous_slices).next,())
ИЛИ ЖЕ:
def chunks_gen_filter(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return takewhile(bool,imap(tuple, continuous_slices))
Я видел самый удивительный ответ на языке Python в дубликате этого вопроса:
from itertools import zip_longest
a = range(1, 16)
i = iter(a)
r = list(zip_longest(i, i, i))
>>> print(r)
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]
Вы можете создать n-кортеж для любого n. Если a = range(1, 15)
, то результатом будет:
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]
Если список разделен поровну, вы можете заменить zip_longest
на zip
, иначе триплет (13, 14, None)
будет потерян. Python 3 используется выше. Для Python 2 используйте izip_longest
.
Еще одно решение
def make_chunks(data, chunk_size):
while data:
chunk, data = data[:chunk_size], data[chunk_size:]
yield chunk
>>> for chunk in make_chunks([1, 2, 3, 4, 5, 6, 7], 2):
... print chunk
...
[1, 2]
[3, 4]
[5, 6]
[7]
>>>
На данный момент, я думаю, нам нужна обязательная анонимно-рекурсивная функция.
Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
chunks = Y(lambda f: lambda n: [n[0][:n[1]]] + f((n[0][n[1]:], n[1])) if len(n[0]) > 0 else [])
Код:
def split_list(the_list, chunk_size):
result_list = []
while the_list:
result_list.append(the_list[:chunk_size])
the_list = the_list[chunk_size:]
return result_list
a_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print split_list(a_list, 3)
Результат:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
Еще одна более явная версия.
def chunkList(initialList, chunkSize):
"""
This function chunks a list into sub lists
that have a length equals to chunkSize.
Example:
lst = [3, 4, 9, 7, 1, 1, 2, 3]
print(chunkList(lst, 3))
returns
[[3, 4, 9], [7, 1, 1], [2, 3]]
"""
finalList = []
for i in range(0, len(initialList), chunkSize):
finalList.append(initialList[i:i+chunkSize])
return finalList
Так как все здесь говорят об итераторах. boltons
предлагает для этого идеальный метод, который называется iterutils.chunked_iter
.
from boltons import iterutils
list(iterutils.chunked_iter(list(range(50)), 11))
Выход:
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
[22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
[44, 45, 46, 47, 48, 49]]
Но если вы не хотите пощадить память, вы можете использовать старый способ и в первую очередь сохранить полный list
с помощью iterutils.chunked
.
Вот генератор, который работает с произвольными итерациями:
def split_seq(iterable, size):
it = iter(iterable)
item = list(itertools.islice(it, size))
while item:
yield item
item = list(itertools.islice(it, size))
Примере:
>>> import pprint
>>> pprint.pprint(list(split_seq(xrange(75), 10)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
Простой, но элегантный
l = range(1, 1000)
print [l[x:x+10] for x in xrange(0, len(l), 10)]
Или если вы предпочитаете:
chunks = lambda l, n: [l[x: x+n] for x in xrange(0, len(l), n)]
chunks(l, 10)
Если вы знаете размер списка:
def SplitList(mylist, chunk_size):
return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]
Если нет (итератор):
def IterChunks(sequence, chunk_size):
res = []
for item in sequence:
res.append(item)
if len(res) >= chunk_size:
yield res
res = []
if res:
yield res # yield the last, incomplete, portion
В последнем случае его можно перефразировать более красивым способом, если вы можете быть уверены, что последовательность всегда содержит целое количество фрагментов заданного размера (то есть не существует неполного последнего фрагмента).
Вы также можете использовать get_chunks
функцию utilspie
библиотека как:
>>> from utilspie import iterutils
>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(iterutils.get_chunks(a, 5))
[[1, 2, 3, 4, 5], [6, 7, 8, 9]]
Вы можете установить utilspie
через pip:
sudo pip install utilspie
Отказ от ответственности: я являюсь создателем utilspie библиотеки .
Мне очень нравится версия документа Python, предложенная tzot и J.F.Sebastian, но у нее есть два недостатка:
- это не очень явно
- Я обычно не хочу, чтобы значение заполнения в последнем чанке
Я использую это много в моем коде:
from itertools import islice
def chunks(n, iterable):
iterable = iter(iterable)
while True:
yield tuple(islice(iterable, n)) or iterable.next()
ОБНОВЛЕНИЕ: версия ленивых кусков:
from itertools import chain, islice
def chunks(n, iterable):
iterable = iter(iterable)
while True:
yield chain([next(iterable)], islice(iterable, n-1))
Вот список дополнительных подходов:
< Сильный > Учитывая
import itertools as it
import collections as ct
import more_itertools as mit
iterable = range(11)
n = 3
< Сильный > Код
Стандартная библиотека
list(it.zip_longest(*[iter(iterable)] * n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
d = {}
for i, x in enumerate(iterable):
d.setdefault(i//n, []).append(x)
list(d.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
dd = ct.defaultdict(list)
for i, x in enumerate(iterable):
dd[i//n].append(x)
list(dd.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
list(mit.chunked(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
list(mit.sliced(iterable, n))
# [range(0, 3), range(3, 6), range(6, 9), range(9, 11)]
list(mit.grouper(n, iterable))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
list(mit.windowed(iterable, len(iterable)//n, step=n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
< STRONG> Ссылки
zip_longest
(связанный пост, связанный пост)setdefault
(для упорядоченных результатов требуется Python 3.6+)collections.defaultdict
(для упорядоченных результатов требуется Python 3.6+)more_itertools.chunked
(связанный опубликовал)more_itertools.sliced
-
more_itertools.grouper
(связанный пост) more_itertools.windowed
(см. такжеstagger
,zip_offset
)
+ Сторонняя библиотека, которая реализует рецепты itertools и многое другое. > pip install more_itertools
Непосредственно из (старой) документации Python (рецепты для itertools):
from itertools import izip, chain, repeat
def grouper(n, iterable, padvalue=None):
"grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)
Текущая версия, предложенная J.F.Sebastian:
#from itertools import izip_longest as zip_longest # for Python 2.x
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)
def grouper(n, iterable, padvalue=None):
"grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)
Я думаю, машина времени Гвидо работает - работала - будет работать - будет работать - снова работала.
Эти решения работают, потому что [iter(iterable)]*n
(или эквивалент в более ранней версии) создает один итератор, повторенный n
раз в списке. izip_longest
затем эффективно выполняет циклический перебор "каждого" итератора; поскольку это один и тот же итератор, он добавляется при каждом таком вызове, в результате чего каждый такой zip-циклический цикл генерирует один кортеж из n
элементов.
Если вы хотите что-то супер простое:
def chunks(l, n):
n = max(1, n)
return (l[i:i+n] for i in range(0, len(l), n))
Используйте xrange()
вместо range()
в случае Python 2.x
Я удивлен, что никто не думал об использовании iter
двух аргументов сформировать :
from itertools import islice
def chunk(it, size):
it = iter(it)
return iter(lambda: tuple(islice(it, size)), ())
Демо-версия :
>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
Это работает с любой итерацией и производит ленивый вывод. Он возвращает кортежи, а не итераторы, но, тем не менее, он обладает определенной элегантностью. Это также не дополняет; если вы хотите заполнить, достаточно простого варианта выше:
from itertools import islice, chain, repeat
def chunk_pad(it, size, padval=None):
it = chain(iter(it), repeat(padval))
return iter(lambda: tuple(islice(it, size)), (padval,) * size)
Демо-версия :
>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]
Как и в решениях на основе izip_longest
, вышеприведенные пэды всегда . Насколько я знаю, не существует одно- или двухстрочного рецепта itertools для функции, которая может быть дополнена . Комбинируя два вышеупомянутых подхода, этот подход довольно близок:
_no_padding = object()
def chunk(it, size, padval=_no_padding):
if padval == _no_padding:
it = iter(it)
sentinel = ()
else:
it = chain(iter(it), repeat(padval))
sentinel = (padval,) * size
return iter(lambda: tuple(islice(it, size)), sentinel)
Демо-версия :
>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]
Я полагаю, что это самый короткий предложенный кусок, который предлагает дополнительное заполнение.
Как заметил Томас Гандор , два блока заполнения неожиданно остановятся, если они встретят длинную последовательность значений полей. Вот последний вариант, который разумным образом решает эту проблему:
_no_padding = object()
def chunk(it, size, padval=_no_padding):
it = iter(it)
chunker = iter(lambda: tuple(islice(it, size)), ())
if padval == _no_padding:
yield from chunker
else:
for ch in chunker:
yield ch if len(ch) == size else ch + (padval,) * (size - len(ch))
Демо-версия :
>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]
Похожие вопросы
Связанные вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.