在Python中获取生成器的第n项

Oli*_*eng 56 python generator

是否有更复杂的语法写作方式?

gen = (i for i in xrange(10))
index = 5
for i, v in enumerate(gen):
    if i is index:
        return v
Run Code Online (Sandbox Code Playgroud)

生成器应该有一个gen[index]表达式,作为一个列表,但在功能上与上面的代码完全相同似乎是很自然的.

cob*_*bal 55

一种方法是使用 itertools.islice

>>> gen = (x for x in range(10))
>>> index = 5
>>> next(itertools.islice(gen, index, None))
5
Run Code Online (Sandbox Code Playgroud)

  • 将`5 + 1`替换为'None`它也可以工作,读起来更简单 (10认同)

Mar*_*ers 15

您可以使用count作为示例生成器来执行此操作:

from itertools import islice, count
next(islice(count(), n, n+1))
Run Code Online (Sandbox Code Playgroud)

  • 所以最好使用`next(islice(count(),n,n + 1))`. (2认同)

lov*_*soa 11

我认为最好的方法是:

next(x for i,x in enumerate(it) if i==n)
Run Code Online (Sandbox Code Playgroud)

it你的迭代器在哪里n,索引在哪里)

它不需要您添加导入(如使用 的解决方案itertools),也不需要一次性将迭代器的所有元素加载到内存中(如使用 的解决方案list)。

注意 1:StopIteration如果您的迭代器少于 n 个项目,此版本会引发错误。如果你想得到None,你可以使用:

next((x for i,x in enumerate(it) if i==n), None)
Run Code Online (Sandbox Code Playgroud)

注 2: 调用中没有括号next。这不是列表推导式,而是生成器推导式,它不会消耗原始迭代器超过其第 n 个元素。

  • @ubershmekel:不,不会!它将遍历前 n 个元素(当然),仅此而已。你为什么不自己试试呢? (2认同)

Bra*_*son 6

我反对将生成器视为列表的诱惑。简单但幼稚的方法是简单的单行:

gen = (i for i in range(10))
list(gen)[3]
Run Code Online (Sandbox Code Playgroud)

但请记住,生成器不像列表。他们不会在任何地方存储他们的中间结果,所以你不能倒退。我将在 python repl 中用一个简单的例子来演示这个问题:

>>> gen = (i for i in range(10))
>>> list(gen)[3]
3
>>> list(gen)[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
Run Code Online (Sandbox Code Playgroud)

一旦您开始通过生成器获取序列中的第 n 个值,生成器现在处于不同的状态,再次尝试获取第 n 个值将返回不同的结果,这可能会导致您的错误代码。

让我们看另一个例子,基于问题中的代码。

人们最初会期望以下内容打印4两次。

gen = (i for i in range(10))
index = 4
for i, v in enumerate(gen):
    if i == index:
        answer = v
        break
print(answer)
for i, v in enumerate(gen):
    if i == index:
        answer = v
        break
print(answer)
Run Code Online (Sandbox Code Playgroud)

但是在 repl 中输入这个,你会得到:

>>> gen = (i for i in range(10))
>>> index = 4
>>> for i, v in enumerate(gen):
...     if i == index:
...             answer = v
...             break
... 
>>> print(answer)
4
>>> for i, v in enumerate(gen):
...     if i == index:
...             answer = v
...             break
... 
>>> print(answer)
9
Run Code Online (Sandbox Code Playgroud)

祝你好运追踪那个错误。

编辑:

正如所指出的,如果生成器是无限长的,您甚至无法将其转换为列表。表达式list(gen)永远不会结束。

有一种方法可以在无限生成器周围放置一个懒惰评估的缓存包装器,使其看起来像一个无限长的列表,您可以随意索引,但这值得它自己的问题和答案,并且会对性能产生重大影响。

  • 如果生成器是无限的呢? (5认同)
  • 这应该更高,因为这样做会在时间上付出巨大的代价。谢谢你指出这一点。 (2认同)