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


213

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

def myfunct():
  ...
  yield result

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

for r in myfunct():
  dostuff(r)

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

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

Answers:


304

使用创建一个生成器

g = myfunct()

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

next(g)

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

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

next(g, default_value)

4
请注意,只有在提供g中的最后一项后尝试使用g.next()时,它才会引发StopIteration。
野鸭

26
next(gen, default)也可以用来避免StopIteration异常。例如,next(g, None)对于字符串生成器,在迭代完成后将生成字符串或“无”。
Attila 2013年

8
在Python 3000中,next()是__next __()
Jonathan Baldwin 2014年

27
@JonathanBaldwin:您的评论有些误导。在Python 3中,您将使用答案中给出的第二种语法next(g)。这将在内部调用g.__next__(),但是您实际上不必担心,就像您通常不关心在len(a)内部调用一样a.__len__()
Sven Marnach 2014年

14
我应该更清楚了。g.next()g.__next__()在py3k。next(iterator)自python 2.6起就内置了该内建函数,所有新的Python代码都应使用该内建函数,如果需要支持py <= 2.5,则对后实现来说是微不足道的。
乔纳森·鲍德温

29

要仅选择生成器的一个元素,请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是常规的。


8
仅获取列表结果的第一个元素就需要太多工作。通常我不需要懒惰,但是在py3中没有选择。有没有类似的东西mySeq.head
javadba

2

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


1

对于那些浏览这些答案的人来说,它们是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

1

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)

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

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


对于Python 3无效,请参见kxr的出色回答
clacke

2
只需将Python 3的“ generator.next()”替换为“ next(generator)”
即可。– iyop45

-3

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

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

Sven的答案可能会更好,但是我将在此保留,以防它更符合您的需求。
keegan3d 2011年

26
在执行此操作之前,请确保您具有有限生成器。
塞斯,

6
抱歉,迭代器的长度很复杂,而问题显然是O(1)。
2014年

1
浪费太多的内存和进程来吸引发电机!另外,正如前面提到的@Seth一样,不能保证何时停止生成生成器。
pylover

显然,这不是唯一的方法(如果myfunct()生成大量值,则不是最佳方法),因为您可以使用内置函数next来获取下一个生成的值。
HelloGoodbye
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.