【发布时间】:2018-12-28 23:44:36
【问题描述】:
如果你在 Python 3.7 中有一个列表:
>>> li
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
您可以使用两个常见的 Python 习惯用法之一将其转换为每个长度为 n 的块列表:
>>> 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中使用哨兵,但iter的哨兵版本需要可调用,而不是可迭代。
是否有更惯用的 Python 3 技术来生成长度为 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 年的历史了,并且专注于一个列表。 Python 3 中没有带有生成器的 new 方法,您不知道长度并且一次只想要一个块?
【问题讨论】:
-
可能我理解错了,但是
islice有什么问题,比如for item in gen: print(tuple(islice(gen,3)))(当然,将print替换为yield作为生成器函数) -
@Kasramvd:啊,是的——我的答案只是senderle's,默认值是一行。
-
@Kasramvd:我不认为这些是完全重复的,因为 1)主要与内存中已经存在的列表有关,或者 2)不采用 Python 3.6+ 的新功能和 3)有一些变体我列出的两个成语。链接的问题是 10 岁。我们是否断定没有新的 Python 3 唯一方法可以做到这一点?
标签: python python-3.x generator