如果你在Python 3.7中有一个列表:
>>> li
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
您可以将其转换为一个块列表,每个块的长度为n
,并使用两种常见的Python语言之一:
>>> n=3
>>> list(zip(*[iter(li)]*n))
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]
由于(9,10)
不是长度为n
的元组,因此将丢弃最后一个不完整的元组
您还可以执行以下操作:
>>> [li[i:i+n] for i in range(0,len(li),n)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
如果你想要最后一个子列表,即使它的元素少于n
。
假设现在我有一个生成器,gen
,未知的长度或终止(所以调用list(gen))
或sum(1 for _ in gen)
将不是明智之举),我想要每个块。
我所能想到的最好的生成器表达式是这样的:
from itertools import zip_longest
sentinel=object() # for use in filtering out ending chunks
gen=(e for e in range(22)) # fill in for the actual gen
g3=(t if sentinel not in t else tuple(filter(lambda x: x != sentinel, t)) for t in zip_longest(*[iter(gen)]*n,fillvalue=sentinel))
为预期的目的工作:
>>> next(g3)
(0, 1, 2)
>>> next(g3)
(3, 4, 5)
>>> list(g3)
[(6, 7, 8), (9, 10)]
只是看起来--笨拙。我试过了:
islice
但是长度的缺乏似乎很难在iter
中调用一个islice
,但是iter
的前哨版本需要一个可调用的,而不是一个可迭代的。有没有一种更常用的Python3技术来生成长度为n
的块,包括可能小于n
的最后一个块
我也对生成器函数持开放态度。我正在寻找一些地道的和大多数更具可读性的东西。
更新:
DSM在他删除的答案中的方法非常好,我认为:
>>> g3=(iter(lambda it=iter(gen): tuple(islice(it, n)), ()))
>>> next(g3)
(0, 1, 2)
>>> list(g3)
[(3, 4, 5), (6, 7, 8), (9, 10)]
作为一个dup,我对这个问题持开放态度,但这个链接的问题已经有10年的历史了,并且集中在一个列表上。在Python3的生成器中没有新的方法,你不知道它的长度,并且一次只想要一个块吗?
发布于 2018-07-21 00:09:18
我认为只要你想把它放在一个线条里,这就会变得一团糟。我会咬紧牙关,在这里使用一个生成器函数。如果您不知道实际大小(例如,如果gen
是无限生成器等),则特别有用。
from itertools import islice
def chunk(gen, k):
"""Efficiently split `gen` into chunks of size `k`.
Args:
gen: Iterator to chunk.
k: Number of elements per chunk.
Yields:
Chunks as a list.
"""
while True:
chunk = [*islice(gen, 0, k)]
if chunk:
yield chunk
else:
break
>>> gen = iter(list(range(11)))
>>> list(chunk(gen))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
有人可能有更好的建议,但这就是我要做的。
发布于 2018-07-21 00:16:50
这感觉像是一种构建在itertools上的非常合理的方法。
>>> g = (i for i in range(10))
>>> g3 = takewhile(lambda x: x, (list(islice(g,3)) for _ in count(0)))
>>> list(g3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
发布于 2018-07-22 01:47:59
我在这里整理了一些答案的时间。
我最初编写它的方式实际上是Python3.7上最快的。对于one liner来说,这可能是最好的。
修改后的cold speed的answer既快速,又具有Python化和可读性。
其他的答案都是相似的速度。
基准测试:
from __future__ import print_function
try:
from itertools import zip_longest, takewhile, islice, count
except ImportError:
from itertools import takewhile, islice, count
from itertools import izip_longest as zip_longest
from collections import deque
def f1(it,k):
sentinel=object()
for t in (t if sentinel not in t else tuple(filter(lambda x: x != sentinel, t)) for t in zip_longest(*[iter(it)]*k, fillvalue=sentinel)):
yield t
def f2(it,k):
for t in (iter(lambda it=iter(it): tuple(islice(it, k)), ())):
yield t
def f3(it,k):
while True:
chunk = (*islice(it, 0, k),) # tuple(islice(it, 0, k)) if Python < 3.5
if chunk:
yield chunk
else:
break
def f4(it,k):
for t in takewhile(lambda x: x, (tuple(islice(it,k)) for _ in count(0))):
yield t
if __name__=='__main__':
import timeit
def tf(f, k, x):
data=(y for y in range(x))
return deque(f(data, k), maxlen=3)
k=3
for f in (f1,f2,f3,f4):
print(f.__name__, tf(f,k,100000))
for case, x in (('small',10000),('med',100000),('large',1000000)):
print("Case {}, {:,} x {}".format(case,x,k))
for f in (f1,f2,f3,f4):
print(" {:^10s}{:.4f} secs".format(f.__name__, timeit.timeit("tf(f, k, x)", setup="from __main__ import f, tf, x, k", number=10)))
结果是:
f1 deque([(99993, 99994, 99995), (99996, 99997, 99998), (99999,)], maxlen=3)
f2 deque([(99993, 99994, 99995), (99996, 99997, 99998), (99999,)], maxlen=3)
f3 deque([(99993, 99994, 99995), (99996, 99997, 99998), (99999,)], maxlen=3)
f4 deque([(99993, 99994, 99995), (99996, 99997, 99998), (99999,)], maxlen=3)
Case small, 10,000 x 3
f1 0.0125 secs
f2 0.0231 secs
f3 0.0185 secs
f4 0.0250 secs
Case med, 100,000 x 3
f1 0.1239 secs
f2 0.2270 secs
f3 0.1845 secs
f4 0.2477 secs
Case large, 1,000,000 x 3
f1 1.2140 secs
f2 2.2431 secs
f3 1.7967 secs
f4 2.4697 secs
https://stackoverflow.com/questions/51446327
复制相似问题