使用生成器发送方法。仍然试图了解发送方法和古怪的行为

时间:2013-11-10 16:37:33

标签: python python-2.7

这是我写的一个小函数来理解send方法:

>>> 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)

为什么第一次出现错误?

>>> a.send(0)
StopIteration 

为什么第一个send()需要为None?与此错误一样:

>>> 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

然后第一次发送启动发生器(我不知道为什么)并且我发送'0'并且它打印它但是第二次0再次没有并且恢复我发送的任何东西(0在这里)

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

此链接对Python 3: send method of generators

的帮助不大

3 个答案:

答案 0 :(得分:7)

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

第一次没有错误,在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

答案 1 :(得分:0)

send()仅在生成器正在等待收益时才可以调用。由于您的生成器尚未开始执行,因此调用send()会给您带来第一个错误。

此外,生成器中的每个收益都会消耗send()发送的值。当第二个yield语句使用值但不使用它时,该值将被丢弃。然后等待第一个收益率的使用,该收益率确实消耗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                           

答案 2 :(得分:0)

PEP 342 New generator method: send(value)说:

调用send(None)等同于调用生成器的 next()方法。用其他任何值调用send()都是一样的,除了 发电机当前收益率表达式产生的值 会有所不同。

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