如果您在 Python 3.7 中有一个列表:
>>> li
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
您可以使用两个常见的 Python 习语之一将其转换为每个长度为 n
的 block 列表:
>>> 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)
不明智)我想要每个 block 。
我能想出的最好的生成器表达式是这样的:
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
中使用哨兵,但iter
的哨兵版本需要可调用对象,而不是可迭代对象。
是否有更惯用的 Python 3 技术来生成长度为 n
的 block ,包括可能小于 n
的最后一个 chuck ?
我也对生成器函数持开放态度。我正在寻找惯用的东西,而且大多更具可读性。
更新:
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 年的历史了,并且集中在一个列表上。 Python 3 中没有新 方法生成器,您不知道长度并且一次不想超过一个 block ?
最佳答案
我认为,只要您试图将它装入一个衬里,它就会总是很困惑。
我会硬着头皮在这里使用生成器函数。如果您不知道实际大小(例如,如果 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]]
有人可能有更好的建议,但我会这样做。
关于Python 3 生成器理解生成 block ,包括最后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51446327/