Send method using generator. still trying to understand the send method and quirky behaviour

后端 未结 2 1460
生来不讨喜
生来不讨喜 2020-12-09 19:11

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

>>> def test():
...     for x in xrange(10):
...         res = yield
...         y         


        
相关标签:
2条回答
  • 2020-12-09 19:38

    Question 1: Why is there an Error the first time?

    There is no Error the first time, tested on python2.7 and 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
    

    Question 2: Why does it require for the first send() to be None?

    You can't send() a value the first time because the generator did not execute until the point where you have the yield statement, so there is nothing to do with the value.

    Here is the relevant paragraph from the pep that introduced the feature of co-routines with generators (http://www.python.org/dev/peps/pep-0342/):

    Because generator-iterators begin execution at the top of the generator's function body, there is no yield expression to receive a value when the generator has just been created. Therefore, calling send() with a non-None argument is prohibited when the generator iterator has just started, and a TypeError is raised if this occurs (presumably due to a logic error of some kind). Thus, before you can communicate with a coroutine you must first call next() or send(None) to advance its execution to the first yield expression

    A tiny walkthrough:

    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
    
    0 讨论(0)
  • 2020-12-09 19:38

    The send() can only be called if generator is waiting at yield. Since your generator hasn't started executing, calling send() gives you the first error.

    Further more, each yield in the generator consumes the value sent by send(). When the second yield statement consumes value but it doesn't use it so that value is discarded. Then you wait on first yield which does consumes value from send() and that value is printed out. So you end up with needing two send().

    Here's the fixed version:

    >>> 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                           
    
    0 讨论(0)
提交回复
热议问题