Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.

Generators and their next() and send() methods

Yametazamwa mara moja
Ruka hadi kwenye ujumbe wa kwanza ambao haujasomwa

Thomas Mlynarczyk

hayajasomwa,
15 Nov 2008, 13:00:2915/11/2008
kwa
Hello,

I was playing around a bit with generators using next() and send(). And
I was wondering why an extra send() method was introduced instead of
simply allowing an argument for next().

Also, I find it a bit counter-intuitive that send(42) not only "sets"
the generator to the specified value, but yields the next value at the
same time.

As an exercise, I wanted to somehow "modify" a generator so that it
would have a next() method accepting an argument, something like

@myway
def gen():
pass

But I failed to come up with an implementation of the myway() function.

Any comments and/or suggestions?

Greetings,
Thomas

--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)

Arnaud Delobelle

hayajasomwa,
15 Nov 2008, 13:24:2715/11/2008
kwa
Thomas Mlynarczyk <tho...@mlynarczyk-webdesign.de> writes:

> Hello,
>
> I was playing around a bit with generators using next() and
> send(). And I was wondering why an extra send() method was introduced
> instead of simply allowing an argument for next().
>
> Also, I find it a bit counter-intuitive that send(42) not only "sets"
> the generator to the specified value, but yields the next value at the
> same time.

If you want to simply 'set' the generator (by which I take you mean
'change its state') without without iterating it one step, then what you
need is a class with an __iter__() method. Then you can change the
state of the object between calls to next(). E.g.

>>> class MyGenerator(object):
... def __init__(self, v): self.v = v
... def __iter__(self):
... for x in range(10):
... yield self.v
...
>>> g = MyGenerator(5)
>>> for i in g:
... g.v = input("val:")
... print i
...
val:4
5
val:10
4
val:34
10
val:56
34
val:

Etc...

--
Arnaud

Thomas Mlynarczyk

hayajasomwa,
16 Nov 2008, 16:36:0716/11/2008
kwa
Arnaud Delobelle schrieb:

> If you want to simply 'set' the generator (by which I take you mean
> 'change its state') without without iterating it one step, then what you
> need is a class with an __iter__() method. Then you can change the
> state of the object between calls to next(). E.g.

>>>> class MyGenerator(object):
> ... def __init__(self, v): self.v = v
> ... def __iter__(self):
> ... for x in range(10):
> ... yield self.v
> ...
>>>> g = MyGenerator(5)
>>>> for i in g:
> ... g.v = input("val:")
> ... print i

That's a good idea, thanks! Meanwhile I have succeeded in writing a
decorator which allows an argument for next():

def myway( g ):
class mygen( object ):
def __init__( self, n ):
self.g = g( n )
def __iter__( self ):
return self
def next( self, s = None ):
return self.g.next() if s is None else self.g.send( s )
def send( self, s ):
return self.g.send( s )
return mygen

@myway
def gen( n ):
i = 0
while i <= n:
s = yield i
if s is not None:
i = s
else:
i += 1

Of course, this is just an exercise -- it doesn't look as if it's worth
using @mayway in any "real" code just to be able to write g.next(42)
instead of g.send(42). Still, I would like to know why it was decided to
introduce a send() method instead of allowing an argument for next().

Thanks and greetings,

alex23

hayajasomwa,
16 Nov 2008, 19:43:5816/11/2008
kwa
On Nov 17, 7:36 am, Thomas Mlynarczyk <tho...@mlynarczyk-webdesign.de>
wrote:

> Still, I would like to know why it was decided to
> introduce a send() method instead of allowing an argument for next().

Hey Thomas,

A great place to gain insight into the reasoning behind changes to
Python is the PEPs:

http://www.python.org/dev/peps/pep-0342/

That links to the original proposal to extend the generator behaviour
to include send. I haven't the time right now to read it but if
anything is going to explain the choice, it'll be there.

At a guess, I'd expect a new method was chosen to provide semantic
distinctness with the original behaviour. Or maybe so it wouldn't
break any existing code with decorators such as your's :)

Aaron Brady

hayajasomwa,
16 Nov 2008, 21:27:5016/11/2008
kwa
On Nov 16, 3:36 pm, Thomas Mlynarczyk <tho...@mlynarczyk-webdesign.de>
wrote:

> Arnaud Delobelle schrieb:
>
> > If you want to simply 'set' the generator (by which I take you mean
> > 'change its state') without without iterating it one step, then what you
> > need is a class with an __iter__() method.  Then you can change the
> > state of the object between calls to next().  E.g.
> >>>> class MyGenerator(object):
> > ...     def __init__(self, v): self.v = v
> > ...     def __iter__(self):
> > ...         for x in range(10):
> > ...             yield self.v
> > ...
> >>>> g = MyGenerator(5)
> >>>> for i in g:
> > ...     g.v = input("val:")
> > ...     print i
>
> That's a good idea, thanks! Meanwhile I have succeeded in writing a
> decorator which allows an argument for next():

Here's a different structure if you want it. Untested.

def myway( g ):
def init( *ar, **kw ):
g= g( *ar, **kw )
     class mygen( object ):


         def __iter__( self ):
             return self
         def next( self, s= None ):

             return g.next() if s is None else g.send( s )


         def send( self, s ):

             return g.send( s )
     return mygen
return init

And, if you don't intend to use 'myway' on 'listiterator's and such,
'send( None )' is equivalent to 'next( )'.

def myway( g ):
def init( *ar, **kw ):
g= g( *ar, **kw )
class mygen( object ):


def __iter__( self ):
return self
def next( self, s= None ):

return g.send( s )


def send( self, s ):

return g.send( s )
return mygen
return init

There's probably a typo in it.

Thomas Mlynarczyk

hayajasomwa,
18 Nov 2008, 18:14:1418/11/2008
kwa
alex23 schrieb:

> http://www.python.org/dev/peps/pep-0342/
> That links to the original proposal to extend the generator behaviour

After some searching, I found this as a remark in parentheses:

"Introducing a new method instead of overloading next() minimizes
overhead for simple next() calls."

And also a (little) more detailed explanation. So, basically, allowing
an argument for next() would have meant more overhead in the Python
implementation. Still, I don't quite see why, as this could be detected
at compile time, couldn't it?

On the other hand, if I understood correctly, I can use send(None) as an
equivalent of next(). So, while I cannot have next(argument) instead of
send(argument), I can have send(None) instead of next().

> At a guess, I'd expect a new method was chosen to provide semantic
> distinctness with the original behaviour.

But the semantics would not have changed, they would only be "extended"
and thus remain "compatible", wouldn't they?

Greetings,

Thomas Mlynarczyk

hayajasomwa,
18 Nov 2008, 18:20:1118/11/2008
kwa
Aaron Brady schrieb:

> And, if you don't intend to use 'myway' on 'listiterator's and such,
> 'send( None )' is equivalent to 'next( )'.

I didn't know that. But doesn't that impose a restriction somehow? It
makes it impossible to send a None to a generator.

Greetings,

Aaron Brady

hayajasomwa,
18 Nov 2008, 20:08:1418/11/2008
kwa
On Nov 18, 5:20 pm, Thomas Mlynarczyk <tho...@mlynarczyk-webdesign.de>
wrote:

> Aaron Brady schrieb:
>
> > And, if you don't intend to use 'myway' on 'listiterator's and such,
> > 'send( None )' is equivalent to 'next( )'.
>
> I didn't know that. But doesn't that impose a restriction somehow? It
> makes it impossible to send a None to a generator.

No, None is a valid value to use with send. Not all iterators support
send.

Ujumbe 0 mpya