返回生成器以及Python 3.3中的yield

在Python 2中,函数定义中return和yield一起出现错误。 但是对于Python 3.3中的这段代码

def f():
  return 3
  yield 2

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

没有错误,return与函数一起使用yield。 但是,当调用函数__next__时,将引发StopIteration异常。 为什么不仅有返回值3? 这个回报以某种方式被忽略了吗?

scdmb asked 2020-06-26T18:54:40Z
3个解决方案
50 votes

这是Python 3.3的新功能(如注释所述,它甚至在3.2中也不起作用)。 类似于发电机中的1早已等效于2,发电机中的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

Mark Amery answered 2020-06-26T18:54:58Z
27 votes

返回值不会被忽略,但是生成器仅产生值,next()只是结束生成器,在这种情况下会更早。 在这种情况下,推进生成器永远不会达到.__next__()语句。

每当迭代器达到要产生的值的“末尾”时,必须引发next()。 生成器也不例外。 从Python 3.3开始,任何.__next__()表达式都将成为异常的值:

>>> 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))
Martijn Pieters answered 2020-06-26T18:55:28Z
0 votes

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

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

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]

Hubert Grzeskowiak answered 2020-06-26T18:56:05Z
translate from https://stackoverflow.com:/questions/16780002/return-in-generator-together-with-yield-in-python-3-3