13

Here is a little function i wrote to understand the send method:

>>> def test():
...     for x in xrange(10):
...         res = yield
...         yield res
>>> a = test()
>>> next(a)
>>> next(a)
>>> next(a)
>>> next(a)
>>> a.send(0)
Traceback (most recent call last):
   <ipython-input-220-4abef3782000> in <module>()
StopIteration
>>> a = test()
>>> a.send(0)
Traceback (most recent call last):
   <ipython-input-222-4abef3782000> in <module>()    
TypeError: can't send non-None value to a just-started generator
>>> a.send(None)
>>> a.send(0)
0
>>> a.send(0)
>>> a.send(0)
0
>>> a.send(0)
>>> a.send(0)
0
>>> a.send(0)

Why is there an Error the first time?

>>> a.send(0)
StopIteration 

Why does it require for the first send() to be None? as with this error:

>>> a.send(0)
Traceback (most recent call last):
    <ipython-input-222-4abef3782000> in <module>()
TypeError: can't send non-None value to a just-started generator

And then the first send starts the generator( i dont know why) and i send a '0' and it prints it but the second 0 is again none and resumes with whatever i send it(0 here)

>>> a.send(None)
>>> a.send(0)
0
>>> a.send(0)
>>> a.send(0)
0
>>> a.send(0)
>>> a.send(0)
0

This link doesnt help much Python 3: send method of generators

4

3 回答 3

16

问题1:为什么第一次出现Error?

第一次没有Error,在python2.7和python3.3上测试过:

>>> def test():
...     for x in xrange(10):
...         res = yield
...         yield res
... 
>>> a = test()
>>> next(a)
>>> next(a)
>>> next(a)
>>> next(a)
>>> a.send(0)
>>> a.send(0)
0
>>> a.send(0)
>>> a.send(0)
0

问题 2:为什么要求第一个 send() 为 None?

您第一次无法send()获得值,因为生成器直到您拥有 yield 语句时才执行,因此与该值无关。

这是 pep 中的相关段落,它介绍了带有生成器的协同程序的特性(http://www.python.org/dev/peps/pep-0342/):

因为生成器迭代器在生成器函数体的顶部开始执行,所以当生成器刚刚创建时,没有 yield 表达式来接收值。因此,当生成器迭代器刚刚启动时,禁止使用非 None 参数调用 send(),如果发生这种情况(可能是由于某种逻辑错误),则会引发 TypeError。因此,在与协程通信之前,您必须首先调用 next() 或 send(None) 以将其执行推进到第一个 yield 表达式

一个小小的演练:

def coro():
   print 'before yield'
   a = yield 'the yield value'
   b = yield a
   print 'done!'
 c=coro() # this does not execute the generator, only creates it

 # If you use c.send('a value') here it could _not_ do anything with the value
 # so it raises an TypeError! Remember, the generator was not executed yet,
 # only created, it is like the execution is before the `print 'before yield'`

 # This line could be `c.send(None)` too, the `None` needs to be explicit with
 # the first use of `send()` to show that you know it is the first iteration
 print next(c) # will print 'before yield' then 'the yield value' that was yield

 print c.send('first value sent') # will print 'first value sent'

 # will print 'done!'
 # the string 'the second value sent' is sent but not used and StopIterating will be raised     
 print c.send('the second value sent') 

 print c.send('oops') # raises StopIterating
于 2013-11-10T16:50:36.723 回答
2

send()只能在生成器等待 yield 时调用。由于您的生成器尚未开始执行,因此调用send()会给您第一个错误。

此外,生成器中的每个产量都会消耗 发送的值send()。当第二个 yield 语句使用 value 但它不使用它时,该 value 被丢弃。然后你等待第一个收益,它确实消耗了价值,send()并且该价值被打印出来。所以你最终需要两个send().

这是固定版本:

>>> def echo():            
...   while True:           
...     val = (yield) 
...     yield val           
...                         
>>> g=echo()               
>>> next(g)    # move to 1st yield
>>> g.send(2)  # execution stops at 2nd yield
2                           
>>> next(g)    # execution stops at 1st yield
>>> g.send(3)  # execution stops at 2nd yield      
3                           
于 2018-12-30T07:22:40.483 回答
0

PEP 342 新的生成器方法:send(value)说:

调用 send(None) 完全等同于调用生成器的 next() 方法。使用任何其他值调用 send() 是相同的,只是生成器的当前 yield 表达式产生的值会不同。

因为生成器迭代器从生成器函数体的顶部开始执行,所以当生成器刚刚创建时,没有 yield 表达式来接收值。因此,当生成器迭代器刚刚启动时,禁止使用非 None 参数调用 send(),如果发生这种情况(可能是由于某种逻辑错误),则会引发 TypeError。因此,在与协程通信之前,您必须首先调用 next() 或 send(None) 以将其执行推进到第一个 yield 表达式

于 2020-12-17T06:10:10.897 回答