generator object or 'send' method?

Aaron Brady castironpi at gmail.com
Tue Feb 10 13:39:59 EST 2009


On Feb 10, 6:30 am, "andrew cooke" <and... at acooke.org> wrote:
> steven probably knows this, but to flag the issue for people who are
> looking at generators/coroutines for the first time: there's a little
> "gotcha" about exactly how the two sides of the conversation are
> synchronized.  in simple terms: send also receives.
>
> unfortunately the example steven gave doesn't really show this, so i've
> modified it below.  you can now see that the first next() after .send()
> receives 2, not 1.  note that i am using python 3, so the .next() method
> is .__next__() (the asymmetry between next and send seems odd to me, but
> there you go).
>
> (in a sense this is completely logical, but if you're used to the
> asynchronous way the internet works, it can seem unintuitive)
>
> how to handle this was one of the things the original post was asking
> about (if i understood correctly).
>
> >>> def gen(n):
>
> ...   while True:
> ...     obj = yield n
> ...     n += 1
> ...     if obj is not None: n = obj
> ...>>> g = gen(5)
> >>> next(g)
> 5
> >>> g.__next__()
> 6
> >>> g.send(1)
> 1
> >>> next(g)
>
> 2
>
> andrew
>
> Steven D'Aprano wrote:
> > On Tue, 10 Feb 2009 05:28:26 +0000, John O'Hagan wrote:
> >> I would love to see a simple code example of this if you have one; I've
> >> been wanting to do this but couldn't even get started.
>
> > Is this too simple?
>
> >>>> def gen(n):
> > ...     while True:
> > ...             obj = yield n
> > ...             if obj is not None: n = obj
> > ...
> >>>> g = gen(5)
> >>>> g.next()
> > 5
> >>>> g.next()
> > 5
> >>>> g.send(12)
> > 12
> >>>> g.next()
> > 12
>
>

I guess a generator that counts, but skips K numbers, where K can be
varied.  For instance, you initialize it with N, the starting number,
and K, the jump size.  Then, you can change either one later on.
(Unproduced.)

>>> j= jumper( N= 1, K= 1 )
>>> next( j )
1
>>> next( j )
2
>>> j.send( K= 3 )
5
>>> next( j )
8

However, if in the caller, the 'send' and 'next' come from two
different places, then your 'send' "eats" one of your 'next's, and the
'next' code just receives 1..2..8.

The above code isn't legal (or practical), due to the 'K=3' in send.
You could do 'j.send( dict( K= 3 ) )', but you can't do 'locals
().update( result )' in your generator, unfortunately.  So you have a
big switch statement there.

One solution: You could always query a dictionary for your variables,
and do 'my_locals.update( result )', and 'my_locals[ "K" ]' for a
variable.  Then you just need some sort of collector generator to go
around the first one, and duplicate the value 'send' returns, before
going back to 'next'.  (Unproduced.)

>>> j= repeat_on_send( jumper( N= 1, K= 1 ) )
>>> next( j )
1
>>> next( j )
2
>>> j.send( dict( K= 3 ) )
5
>>> next( j )
5
>>> next( j )
8

Two solution (er, solution two), would be to give a generator object
access.  Unfortunately, generators have no 'gi_dict' attribute, so
arbitrary attributes aren't possible.  So you have to be clever.
(Unproduced.)

>>> @gen_dict
>>> def jumper...
...
>>> j= jumper( N= 1, K= 1 )
>>> next( j )
1
>>> next( j )
2
>>> j.K= 3
>>> next( j )
5
>>> next( j )
8

This gets two birds with one stone, but is it possible?



More information about the Python-list mailing list