我制作了一个生成器来逐字读取文件,效果很好。
def word_reader(file):
for line in open(file):
for p in line.split():
yield p
reader = word_reader('txtfile')
next(reader)
将接下来的 n 个值放入列表的最简单方法是什么?
TL;DR:使用
itertools.islice
。
本来我写了另一个答案,结果证明这是一个坏主意:
[next(it) for _ in range(n)]
当
it
产生小于 n
值时,此行为会崩溃,并且此行为取决于微妙的问题,因此阅读此类代码的人不太可能理解其精确的语义。
next(it)
筋疲力尽并加注 StopIteration
会发生什么?(即当
it
的产量小于 n
值时)
当我几年前写下上面这行代码时,我可能认为
StopIteration
会产生干净地终止列表理解的巧妙副作用。但不,整个理解会因向上传递StopIteration
而崩溃。 (只有当异常源自 range(n)
迭代器时,它才会干净地退出。)
这可能不是您想要的行为。
但情况变得更糟。以下内容应该等同于列表理解(尤其是在 Python 3 上):
list(next(it) for _ in range(n))
事实并非如此。内部部分是生成器函数的简写;
list()
当它升起时就知道它已完成 StopIteration
任何地方。n
值时,此版本可以安全地处理并返回较短的列表。 (就像itertools.islice()
。)
但这也将会改变!事实上,当生成器内部的任何代码引发
StopIteration
时,生成器会默默退出,这是一个已知的问题,由 PEP 479 解决。从 Python 3.7(或将来导入的 3.5)开始,这将导致 RuntimeError
而不是干净地完成生成器。 IE。它会变得类似于列表理解的行为。
(在最近的 HEAD 版本上进行测试)
要获取生成器的前 n 个值,可以使用 more_itertools.take。
如果您计划以块的形式迭代单词(例如一次 100 个),则可以使用 more_itertools.chunked (https://more-itertools.readthedocs.io/en/latest/api.html):
import more_itertools
for words in more_itertools.chunked(reader, n=100):
# process 100 words
for i, word in zip(range(n), word_reader(file)):
...
使用cytoolz.take。
>>> from cytoolz import take
>>> list(take(2, [10, 20, 30, 40, 50]))
[10, 20]