问题:如何从生成器中仅选择一项(在python中)?

我有一个类似下面的生成器函数:

def myfunct():
  ...
  yield result

调用此函数的常用方法是:

for r in myfunct():
  dostuff(r)

我的问题是,有什么方法可以随时从生成器中获取一个元素吗?例如,我想做类似的事情:

while True:
  ...
  if something:
      my_element = pick_just_one_element(myfunct())
      dostuff(my_element)
  ...

I have a generator function like the following:

def myfunct():
  ...
  yield result

The usual way to call this function would be:

for r in myfunct():
  dostuff(r)

My question, is there a way to get just one element from the generator whenever I like? For example, I’d like to do something like:

while True:
  ...
  if something:
      my_element = pick_just_one_element(myfunct())
      dostuff(my_element)
  ...

回答 0

使用创建一个生成器

g = myfunct()

每当您想要一个项目时,请使用

next(g)

(或g.next()在Python 2.5或更低版本中)。

如果生成器退出,它将升高StopIteration。您可以根据需要捕获此异常,也可以将default参数用于next()

next(g, default_value)

Create a generator using

g = myfunct()

Everytime you would like an item, use

next(g)

(or g.next() in Python 2.5 or below).

If the generator exits, it will raise StopIteration. You can either catch this exception if necessary, or use the default argument to next():

next(g, default_value)

回答 1

要仅选择生成器的一个元素,请breakfor语句中使用,或list(itertools.islice(gen, 1))

根据您的示例(从字面上看),您可以执行以下操作:

while True:
  ...
  if something:
      for my_element in myfunct():
          dostuff(my_element)
          break
      else:
          do_generator_empty()

如果您想“ 每当我喜欢的时候就从 [生成的] 生成器中仅获取一个元素 ”(我想是最初意图的50%,也是最常见的意图),那么:

gen = myfunct()
while True:
  ...
  if something:
      for my_element in gen:
          dostuff(my_element)
          break
      else:
          do_generator_empty()

这样generator.next()可以避免显式使用,并且输入结束处理不需要(神秘的)StopIteration异常处理或额外的默认值比较。

else:for,如果你想要做一些特别的结束产生的case语句段时,才需要。

注意上next()/ .next()

在Python3中,该.next()方法被重命名.__next__()为有充分的理由:它被认为是低级的(PEP 3114)。在Python 2.6之前,内置函数next()不存在。甚至讨论过迁移next()到该operator模块(这本来是明智的做法),因为它很少需要,并且内置名称的可疑膨胀。

next()没有默认值的情况下使用仍然是非常低级的实践- StopIteration在普通的应用程序代码中公开地将神秘的东西扔掉。而且使用next()默认的哨兵-最好是next()直接输入的唯一选择builtins-受限制,并且通常会给出奇怪的非Python逻辑/可读性的原因。

底线:很少使用next()-就像使用operator模块的功能一样。使用for x in iteratorislicelist(iterator)等功能接受一个迭代器无缝地使用是在应用层上的迭代器的自然方式-而且相当总是可能的。next()是低级的,一个额外的概念,很明显-正如该线程的问题所示。虽然例如,使用breakfor是常规的。

For picking just one element of a generator use break in a for statement, or list(itertools.islice(gen, 1))

According to your example (literally) you can do something like:

while True:
  ...
  if something:
      for my_element in myfunct():
          dostuff(my_element)
          break
      else:
          do_generator_empty()

If you want “get just one element from the [once generated] generator whenever I like” (I suppose 50% thats the original intention, and the most common intention) then:

gen = myfunct()
while True:
  ...
  if something:
      for my_element in gen:
          dostuff(my_element)
          break
      else:
          do_generator_empty()

This way explicit use of generator.next() can be avoided, and end-of-input handling doesn’t require (cryptic) StopIteration exception handling or extra default value comparisons.

The else: of for statement section is only needed if you want do something special in case of end-of-generator.

Note on next() / .next():

In Python3 the .next() method was renamed to .__next__() for good reason: its considered low-level (PEP 3114). Before Python 2.6 the builtin function next() did not exist. And it was even discussed to move next() to the operator module (which would have been wise), because of its rare need and questionable inflation of builtin names.

Using next() without default is still very low-level practice – throwing the cryptic StopIteration like a bolt out of the blue in normal application code openly. And using next() with default sentinel – which best should be the only option for a next() directly in builtins – is limited and often gives reason to odd non-pythonic logic/readablity.

Bottom line: Using next() should be very rare – like using functions of operator module. Using for x in iterator , islice, list(iterator) and other functions accepting an iterator seamlessly is the natural way of using iterators on application level – and quite always possible. next() is low-level, an extra concept, unobvious – as the question of this thread shows. While e.g. using break in for is conventional.


回答 2

我不认为有一种便捷的方法可以从生成器中检索任意值。生成器将提供next()方法来遍历自身,但是不会立即生成完整序列以节省内存。那就是生成器和列表之间的功能差异。

I don’t believe there’s a convenient way to retrieve an arbitrary value from a generator. The generator will provide a next() method to traverse itself, but the full sequence is not produced immediately to save memory. That’s the functional difference between a generator and a list.


回答 3

对于那些浏览这些答案的人来说,它们是Python3的完整工作示例…在这里,您可以继续:

def numgen():
    x = 1000
    while True:
        x += 1
        yield x

nums = numgen() # because it must be the _same_ generator

for n in range(3):
    numnext = next(nums)
    print(numnext)

输出:

1001
1002
1003

For those of you scanning through these answers for a complete working example for Python3… well here ya go:

def numgen():
    x = 1000
    while True:
        x += 1
        yield x

nums = numgen() # because it must be the _same_ generator

for n in range(3):
    numnext = next(nums)
    print(numnext)

This outputs:

1001
1002
1003

回答 4

Generator是产生迭代器的函数。因此,一旦有了迭代器实例,就可以使用next()从迭代器中获取下一项。例如,使用next()函数来获取第一个项目,然后for in用于处理剩余的项目:

# create new instance of iterator by calling a generator function
items = generator_function()

# fetch and print first item
first = next(items)
print('first item:', first)

# process remaining items:
for item in items:
    print('next item:', item)

Generator is a function that produces an iterator. Therefore, once you have iterator instance, use next() to fetch the next item from the iterator. As an example, use next() function to fetch the first item, and later use for in to process remaining items:

# create new instance of iterator by calling a generator function
items = generator_function()

# fetch and print first item
first = next(items)
print('first item:', first)

# process remaining items:
for item in items:
    print('next item:', item)

回答 5

generator = myfunct()
while True:
   my_element = generator.next()

确保捕获采用最后一个元素后引发的异常

generator = myfunct()
while True:
   my_element = generator.next()

make sure to catch the exception thrown after the last element is taken


回答 6

我相信唯一的方法是从迭代器中获取一个列表,然后从该列表中获取所需的元素。

l = list(myfunct())
l[4]

I believe the only way is to get a list from the iterator then get the element you want from that list.

l = list(myfunct())
l[4]

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。