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

时间:2021-08-29 13:13:20

Is there a more syntactically concise way of writing the following?

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

gen = (i for i in xrange(10))
index = 5
for i, v in enumerate(gen):
    if i is index:
        return v

It seems almost natural that a generator should have a gen[index] expression, that acts as a list, but is functionally identical to the above code.

生成器应该具有gen [index]表达式似乎很自然,它充当列表,但在功能上与上面的代码相同。

6 个解决方案

#1


39  

one method would be to use itertools.islice

一种方法是使用itertools.islice

>>> next(itertools.islice(xrange(10), 5, 5 + 1))
5

#2


14  

You could do this, using count as an example generator:

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

from itertools import islice, count
next(islice(count(), n, n+1))

#3


2  

I'd argue against the temptation to treat generators like lists. The simple but naive approach is the simple one-liner:

我反对对待列表等生成器的诱惑。简单但天真的方法是简单的单线程:

gen = (i for i in range(10))
list(gen)[3]

But remember, generators aren't like lists. They don't store their intermediate results anywhere, so you can't go backwards. I'll demonstrate the problem with a simple example in the python repl:

但请记住,生成器不像列表。他们不会将他们的中间结果存储在任何地方,所以你不能倒退。我将在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

Once you start going through a generator to get the nth value in the sequence, the generator is now in a different state, and attempting to get the nth value again will return you a different result, which is likely to result in a bug in your code.

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

Let's take a look at another example, based on the code from the question.

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

One would initially expect the following to print 4 twice.

人们最初期望以下两次打印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)

but type this into the repl and you get:

但是将它输入到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

Good luck tracing that bug down.

祝你好运追踪那个bug。

EDIT:

编辑:

As pointed out, if the generator is infinitely long, you can't even convert it to a list. The expression list(gen) will never finish.

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

There is a way you could put a lazily evaluated caching wrapper around an infinite generator to make it look like an infinitely long list you could index into at will, but that deserves its own question and answer, and would have major performance implications.

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

#4


0  

The first thing that came to my mind was:

我想到的第一件事是:

gen = (i for i in xrange(10))
index = 5

for i, v in zip(range(index), gen): pass

return v

#5


-1  

Perhaps you should elaborate more on a actual use case.

也许你应该详细说明一个实际的用例。

>>> gen = xrange(10)
>>> ind=5 
>>> gen[ind]
5

#6


-2  

you can just convert the generator into a list and use the index as normal:

您可以将生成器转换为列表并正常使用索引:

>>> [i for i in range(10)][index]
5

#1


39  

one method would be to use itertools.islice

一种方法是使用itertools.islice

>>> next(itertools.islice(xrange(10), 5, 5 + 1))
5

#2


14  

You could do this, using count as an example generator:

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

from itertools import islice, count
next(islice(count(), n, n+1))

#3


2  

I'd argue against the temptation to treat generators like lists. The simple but naive approach is the simple one-liner:

我反对对待列表等生成器的诱惑。简单但天真的方法是简单的单线程:

gen = (i for i in range(10))
list(gen)[3]

But remember, generators aren't like lists. They don't store their intermediate results anywhere, so you can't go backwards. I'll demonstrate the problem with a simple example in the python repl:

但请记住,生成器不像列表。他们不会将他们的中间结果存储在任何地方,所以你不能倒退。我将在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

Once you start going through a generator to get the nth value in the sequence, the generator is now in a different state, and attempting to get the nth value again will return you a different result, which is likely to result in a bug in your code.

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

Let's take a look at another example, based on the code from the question.

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

One would initially expect the following to print 4 twice.

人们最初期望以下两次打印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)

but type this into the repl and you get:

但是将它输入到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

Good luck tracing that bug down.

祝你好运追踪那个bug。

EDIT:

编辑:

As pointed out, if the generator is infinitely long, you can't even convert it to a list. The expression list(gen) will never finish.

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

There is a way you could put a lazily evaluated caching wrapper around an infinite generator to make it look like an infinitely long list you could index into at will, but that deserves its own question and answer, and would have major performance implications.

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

#4


0  

The first thing that came to my mind was:

我想到的第一件事是:

gen = (i for i in xrange(10))
index = 5

for i, v in zip(range(index), gen): pass

return v

#5


-1  

Perhaps you should elaborate more on a actual use case.

也许你应该详细说明一个实际的用例。

>>> gen = xrange(10)
>>> ind=5 
>>> gen[ind]
5

#6


-2  

you can just convert the generator into a list and use the index as normal:

您可以将生成器转换为列表并正常使用索引:

>>> [i for i in range(10)][index]
5