返回生成器以及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
。
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))
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]