考虑以下代码:
def mygen():
yield (yield 1)
a = mygen()
print(next(a))
print(next(a))
输出结果:
1
None
口译员在“外部”到底做了什么?
答案 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))
请注意,此答案仅基于我的观察,在技巧上可能并不正确,欢迎所有更新和建议