By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,301 Members | 1,741 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,301 IT Pros & Developers. It's quick & easy.

Generators and their next() and send() methods

P: n/a
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)
Nov 15 '08 #1
Share this Question
Share on Google+
7 Replies


P: n/a
Thomas Mlynarczyk <th****@mlynarczyk-webdesign.dewrites:
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
Nov 15 '08 #2

P: n/a
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,
Thomas
--
Ce n'est pas parce qu'ils sont nombreux avoir tort qu'ils ont raison!
(Coluche)
Nov 16 '08 #3

P: n/a
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 :)

Nov 17 '08 #4

P: n/a
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.
Nov 17 '08 #5

P: n/a
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

--
Ce n'est pas parce qu'ils sont nombreux avoir tort qu'ils ont raison!
(Coluche)
Nov 18 '08 #6

P: n/a
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,
Thomas

--
Ce n'est pas parce qu'ils sont nombreux avoir tort qu'ils ont raison!
(Coluche)
Nov 18 '08 #7

P: n/a
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.
Nov 19 '08 #8

This discussion thread is closed

Replies have been disabled for this discussion.