产量-产量内部有什么作用?

请考虑以下代码:

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

输出结果:

1
None

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

David asked 2019-11-13T23:17:50Z
4个解决方案
49 votes

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

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

>>> 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语句提供了调用者和生成器之间的单向通信; 调用yield将执行生成器,直到下一个send语句为止,关键字send提供的值将作为next的返回值。也将在next(a)语句的位置暂停,等待对a.send(None)的下一个调用恢复。

在Python 2.5中,yield语句被*替换为yield表达式,并且生成器获取了send方法。 send的工作原理非常类似于next476,但可能会引起争论。 (对于其余部分,假定next(a)a.send(None)等效。)生成器在调用send(None)之后开始执行,直到第一个yield,该生成器将返回之前的值。 但是,现在,表达式将一直阻塞,直到下一次调用send,此时yield表达式的计算结果是传递给send的自变量。生成器现在可以在恢复时接收值。


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

chepner answered 2019-11-13T23:18:43Z
25 votes

yield具有两种形式,即表达式和语句。 它们基本相同,但我最经常看到的是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问题中也有很好的答案:表达式和Python中的语句之间有什么区别?

kojiro answered 2019-11-13T23:19:51Z
3 votes
>>> 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
>>>
Işık Kaplan answered 2019-11-13T23:20:27Z
1 votes

任何生成器都会耗尽元素,直到耗尽它们为止。
在如下所示的2级嵌套示例中,第一个n从最里面的yield给我们元素,即1,下一个yield只返回next,因为它没有要返回的元素,如果再次调用StopIteration,它将返回 StopIteration

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

您可以扩展此案例以包括更多的嵌套收益,您会看到调用n 26130437184206940940之后,会抛出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))

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

Devesh Kumar Singh answered 2019-11-13T23:21:17Z
translate from https://stackoverflow.com:/questions/55922302/what-does-a-yield-inside-a-yield-do