问题描述
我只是在一些嵌套生成器中滥用stopiteration
(使用cpython 3.6.9),没有启用PEP 479(from __future__ import generator_stop
),并且有一些糟糕的hacky代码,使用next(iter(iterable))
表示过早停车。
尽管PEP 479会阻止stopiteration
从生成器中冒出来,但我认为我仍然会在嵌套的for循环中遇到它。
现在,我将用以下内容替换next(iter(...))
的所有用法:
def take(iterable,*,n):
"""
Robustly gets the first n items from an iterable and returns them as a
list.
You should always use this function in lieu of `next(iter(...))`! e.g.
instead of:
my_first = next(iter(container))
you should instead do:
my_first,= take(container,n=1)
Throws RuntimeError if the iterable cannot yield n items.
"""
iterator = iter(iterable)
out = []
for _ in range(n):
try:
out.append(next(iterator))
except stopiteration:
raise RuntimeError("Premature stopiteration encountered!")
return out
我的问题是:Python的stdlib中已经有这样的函数吗?
我在python.org
和itertools
中查看了builtins
的最新文档(3.9版),发现最接近的东西是takewhile
,但是。我还可以转换为list
或任何其他可索引的容器,但我想避免只为访问第一件事就需要遍历所有内容。
解决方法
itertools.islice
可以执行此操作(以及更多操作),而不会转换为列表,也不会产生错误的错误信息。
您可以清楚地根据这一函数编写函数:
def take(iterable,*,n):
li = list(itertools.islice(iterable,n))
if len(li) != n:
raise RuntimeError("too short iterable for take")
return li