收益内的收益有什么作用?

时间:2019-04-30 13:44:45

标签: python python-3.x yield

考虑以下代码:

def mygen():
     yield (yield 1)
a = mygen()
print(next(a))
print(next(a)) 

输出结果:

1
None

口译员在“外部”到底做了什么?

4 个答案:

答案 0 :(得分:48)

a是一个生成器对象。第一次在其上调用next时,将对主体进行评估,直到第一个yield表达式(即第一个要评估的表达式:内部表达式)。 yield产生值1以使next返回,然后阻塞直到下一次进入生成器。这是由对next的第二次调用产生的,该调用 not 不会将任何值发送到生成器中。结果,第一个(内部)yield的值为None。该值用作外部yield的参数,它成为对next的第二次调用的返回值。如果您第三次致电next,则会收到StopIteration异常。

比较使用send方法(而不是next)来更改第一个yield表达式的返回值。

>>> a = mygen()
>>> next(a)
1
>>> a.send(3)  # instead of next(a)
3
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

写生成器的更明确的方式应该是

def mygen():
    x = yield 1
    yield x

a = mygen()
print(a.send(None))  # outputs 1, from yield 1
print(a.send(5))     # makes yield 1 == 5, then gets 5 back from yield x
print(a.send(3))     # Raises StopIteration, as there's nothing after yield x

在Python 2.5之前,yield 语句提供了调用者和生成器之间的单向通信;对next的调用将在下一个yield语句之前执行生成器,并且yield关键字提供的值将作为next的返回值。发电机 也会在yield语句的位置暂停,等待对next的下一次调用恢复。

在Python 2.5中,yield语句被替换为yield 表达式,并且生成器获得了send方法。 send的工作方式与next非常相似,只是它可以带一个参数。 (对于其余情况,假定next(a)等效于a.send(None)。)生成器在调用send(None)之后开始执行,此时它执行直到第一个{{1 }},它像以前一样返回一个值。但是,现在表达式将一直阻塞,直到对yield next 调用为止,此时send表达式的值等于传递给yield的参数。生成器现在可以在恢复时接收一个值。


*尚未完全取代; kojiro的答案更详细地介绍了send语句和yield表达式之间的细微差别。

答案 1 :(得分:25)

yield有两种形式,expressions and statements。它们基本上是相同的,但是我最经常以statement形式看到它们,其中的结果将不会被使用。

def f():
    yield a thing

但是在表达式形式中,yield具有一个值:

def f():
    y = yield a thing

在您的问题中,您同时使用两种形式:

def f():
    yield ( # statement
        yield 1 # expression
    )

迭代生成的生成器时,首先将获得内部yield表达式的结果

>>> x=f()
>>> next(x)
1

这时,内部表达式也产生了一个值,外部语句可以使用

>>> next(x)
>>>  # None

现在您已经用尽了发电机

>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

要了解有关语句和表达式的更多信息,在其他stackoverflow问题中也有很好的答案:What is the difference between an expression and a statement in Python?

答案 2 :(得分:3)

>>> def mygen():
...     yield (yield 1)
...
>>> a = mygen()
>>>
>>> a.send(None)
1
>>> a.send(5)
5
>>> a.send(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>
>>>
>>>
>>> def mygen():
...     yield 1
...
>>> def mygen2():
...     yield (yield 1)
...
>>> def mygen3():
...     yield (yield (yield 1))
...
>>> a = mygen()
>>> a2 = mygen2()
>>> a3 = mygen3()
>>>
>>> a.send(None)
1
>>> a.send(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> a2.send(None)
1
>>> a2.send(0)
0
>>> a2.send(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> a3.send(None)
1
>>> a3.send(0)
0
>>> a3.send(1)
1
>>> a3.send(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

其他所有收益率都只是在等待一个值传递,生成器不仅提供数据,而且还接收数据。


>>> def mygen():
...     print('Wait for first input')
...     x = yield # this is what we get from send
...     print(x, 'is received')
...
>>> a = mygen()
>>> a.send(None)
Wait for first input
>>> a.send('bla')
bla is received
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

yield会在您继续时提供下一个值(如果得到的话),并且如果该值不用于提供下一个值,那么它将用于接收下一个值

>>> def mygen():
...     print('Wait for first input')
...     x = yield # this is what we get from send
...     yield x*2 # this is what we give
...
>>> a = mygen()
>>> a.send(None)
Wait for first input
>>> a.send(5)
10
>>>

答案 3 :(得分:1)

任何生成器都会耗尽元素,直到元素用完。
在如下所示的两层嵌套示例中,第一个next从最里面的yield给我们元素,即1,下一个yields仅返回None,因为它没有要返回的元素,如果您再次致电next,它将返回StopIteration

def mygen():
     yield (yield 1)
a = mygen()
print(next(a))
print(next(a))
print(next(a))

您可以扩展此案例以包括更多的嵌套收益,并且您会看到在调用n next之后,会抛出StopIteration期望,下面是一个具有5个嵌套收益的示例

def mygen():
     yield ( yield ( yield ( yield (yield 1))))
a = mygen()
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))

请注意,此答案仅基于我的观察,在技巧上可能并不正确,欢迎所有更新和建议