在Python 3.3中将生成器与yield一起返回

时间:2013-05-27 20:16:38

标签: python generator

在Python 2中,返回与函数定义中的yield一起出错。但对于Python 3.3中的这段代码

def f():
  return 3
  yield 2

x = f()
print(x.__next__())

没有错误,返回在带有yield的函数中使用。但是,当调用函数__next__时,会抛出异常StopIteration。为什么没有返回值3?这种回报是否被忽略了?

3 个答案:

答案 0 :(得分:48)

这是Python 3.3中的一项新功能(作为评论说明,它甚至不适用于3.2)。与生成器中的return长度相当于raise StopIteration()非常相似,生成器中的return <something>现在等同于raise StopIteration(<something>)。因此,您看到的异常应打印为StopIteration: 3,并且可以通过异常对象上的属性value访问该值。如果生成器被委托使用(也是新的)yield from语法,那么结果就是如此。有关详细信息,请参阅PEP 380

def f():
    return 1
    yield 2

def g():
    x = yield from f()
    print(x)

# g is still a generator so we need to iterate to run it:
for _ in g():
    pass

这会打印1,但不打印2

答案 1 :(得分:24)

不会忽略返回值,但生成器只有 yield 值,return只会结束生成器,在这种情况下就是早期。在这种情况下,推进生成器永远不会到达yield语句。

每当迭代器到达要生成的值的“结束”时,必须引发StopIteration 。发电机也不例外。但是,从Python 3.3开始,任何return表达式都将成为异常的值:

>>> def gen():
...     return 3
...     yield 2
... 
>>> try:
...     next(gen())
... except StopIteration as ex:
...     e = ex
... 
>>> e
StopIteration(3,)
>>> e.value
3

使用next()函数推进迭代器,而不是直接调用.__next__()

print(next(x))

答案 2 :(得分:0)

此答案与问题完全无关,但对于在网络搜索后来到这里的人可能会派上用场。

这是一个小的辅助函数,它将所有最终的返回值转换为产生的值:

def generator():
  yield 1
  yield 2
  return 3

def yield_all(gen):
  while True:
    try:
      yield next(gen)
    except StopIteration as e:
      yield e.value
      break

print([i for i in yield_all(generator())])
  

[1、2、3]


或作为装饰者:

import functools

def yield_all(func):
  gen = func()
  @functools.wraps(func)
  def wrapper():
    while True:
      try:
        yield next(gen)
      except StopIteration as e:
        yield e.value
        break
  return wrapper

@yield_all
def a():
  yield 1
  yield 2
  return 3
print([i for i in a()])
  

[1、2、3]