如何在列表中获取生成器的n个下一个值(python)
我已经做了一个生成器来逐字读取文件,它工作得很好如何在列表中获取生成器的n个下一个值(python),python,list,generator,Python,List,Generator,我已经做了一个生成器来逐字读取文件,它工作得很好 def word_reader(file): for line in open(file): for p in line.split(): yield p reader = word_reader('txtfile') next(reader) 获取列表中n个下一个值的最简单方法是什么?使用itertools.islice: list(itertools.islice(it, n)) 编辑:使
def word_reader(file):
for line in open(file):
for p in line.split():
yield p
reader = word_reader('txtfile')
next(reader)
获取列表中n个下一个值的最简单方法是什么?使用
itertools.islice
:
list(itertools.islice(it, n))
编辑:使用
itertools.islice
。我最初提出的下面的模式是一个坏主意——当it
产生的值小于n
时,它就会崩溃,而这种行为取决于微妙的问题,因此阅读此类代码的人不太可能理解它的精确语义
还有
[next(it) for _ in range(n)]
对于不熟悉itertools的人来说,这可能更清楚;但是,如果您经常使用迭代器,那么itertools是一个值得添加到工具集的工具
如果next(it)
耗尽并引发StopIteration
,会发生什么情况?
(即当时,其
产生的值小于n
时)
当我几年前写这一行时,我可能认为StopIteration
会有一个聪明的副作用,即干净地终止列表理解。但是不,通过向上传递StopIteration
,整个理解将崩溃。(只有当异常源于范围(n)
迭代器时,它才会干净地退出。)
这可能不是你想要的行为
但情况变得更糟了。以下内容应该与列表理解相当(尤其是在Python 3上):
事实并非如此。内部是发电机功能的简写list()
知道在任何地方引发StopIteration
时它就完成了。=>当没有
n
值时,此版本可以安全处理,并返回较短的列表。(如itertools.islice()
)
[执行日期:,]
但这太难改变了!当生成器中的任何代码引发StopIteration
时,生成器会自动退出,这是一个已知的缺点,由解决。从Python3.7(或未来导入的3.5)开始,这将导致运行时错误
,而不是干净地完成生成器。也就是说,它将变得类似于列表的行为。
(在最近的头部构建上测试)要获取生成器的前n个值,可以使用 如果您计划以块的形式迭代单词(例如,一次迭代100个),您可以使用更多的\u itertools.chunked(): 使用
步长值默认为1,因此可以省略:
list(itertools.islice(it,0,n))
@Dave确实如此。也可以省略0,因为它是可选的。思考islice()
的参数的一个简单方法是,它们完全反映range()
的参数:islice([start,]stop[,step])
(有步骤>0的限制)@BeniCherniavsky Paskin:尽管有一个怪癖,在这种情况下,stop
可以显式地设置为None
,这意味着islice
对象本身永远不会停止迭代,除非底层iterable停止。在这种情况下,您试图跳过元素(开始时的初始元素,step-1时的step-1时的elements之间的元素,step>1时的),而不是在足够远的地方截断输入range
不接受None
作为stop
值(itertools.count
填补了这个空缺),因此使用range
的抽象只是一个标题漏洞。是的,也不错。我认为islice解决方案更好一些,所以我会接受它。当然这个答案更好,因为它更简单,不需要额外的模块导入,括号更少。。。也许在Python4中,切片默认返回生成器(与Py3中的映射相比)。我只想将I
更改为\u
,以便在某些IDE中没有“未使用的变量”警告;)。顺便说一句,在Haskell中,它被称为take N
,这是一个完美的函数。除非N大于生成器的长度,否则您将得到一个StopIteration和一个未定义的变量。@xApple oops,您是对的!如果将其写成列表(genartor expr.),则会有令人困惑的不同。为了解释这一点,编辑了向上投票的islice
。如果您不介意伪值,您可以使用next
函数的默认参数并调用,例如[next(it,None)for uu in range(n)]
这是错误的,因为它会消耗生成器中的额外元素。Beni的答案不是这样的。如果您对i、word-in-zip(xrange(n)、word\u阅读器(文件)):
执行,则可以避免这种一次性操作。虽然我更喜欢一个可靠的bug,而不是如此脆弱的依赖顺序的“修复”:-,但这似乎是只使用原语的最简单的方法;我没有标记,因为我需要仔细查看并决定关闭哪一个。我在更多的itertools中查看了take
的源代码,在我看来take
的定义只是list(islice(iterable,n))
。因此,如果您不想为此安装单独的软件包,那么使用islice
解决方案应该没有任何缺点。
list(next(it) for _ in range(n))
for word, i in zip(word_reader(file), xrange(n)):
...
import more_itertools
for words in more_itertools.chunked(reader, n=100):
# process 100 words
>>> from cytoolz import take
>>> list(take(2, [10, 20, 30, 40, 50]))
[10, 20]