def mygen(): while True: print __self__.data yield None
g = mygen() g.data = 1 g.next() # prints 1 g.data = 2 g.next() # prints 2
I looked in the archives but couldn't find a good discussion of why setting an attribute on the generator is preferable to passing the argument to next. Isn't this example basically equivalent to:
class mygen(object): def next(self, data): print data return None
g = mygen() g.next(1) # prints 1 g.next(2) # prints 2
Note that I didn't even define an __iter__ method since it's never used in the example.
Another example from the PEP:
def filelike(packagename, appendOrOverwrite): data = [] if appendOrOverwrite == 'w+': data.extend(packages[packagename]) try: while True: data.append(__self__.dat) yield None except FlushStream: packages[packagename] = data
ostream = filelike('mydest','w') ostream.dat = firstdat; ostream.next() ostream.dat = firstdat; ostream.next() ostream.throw(FlushStream)
This could be rewritten as:
class filelike(object): def __init__(self, packagename, appendOrOverwrite): self.data = [] if appendOrOverwrite == 'w+': self.data.extend(packages[packagename]) def next(self, dat): self.data.append(dat) return None def close(self): packages[packagename] = self.data
ostream = filelike('mydest','w') ostream.next(firstdat) ostream.next(firstdat) ostream.close()
So, I guess I have two questions:
(1) What's the benefit of the generator versions of these functions over the class-based versions?
(2) Since in all the examples there's a one-to-one correlation between setting a generator attribute and calling the generator's next function, aren't these generator attribute assignments basically just trying to define the 'next' parameter list?
If this is true, I would have expected that a more useful idiom would look something like:
def mygen(): while True: data, = nextargs() print data yield None
g = mygen() g.next(1) # prints 1 g.next(2) # prints 2
where the nextargs function retrieves the arguments of the most recent call to the generator's next function.
With a little sys._getframe hack, you can basically get this behavior now:
py> class gen(object): ... def __init__(self, gen): ... self.gen = gen ... def __iter__(self): ... return self ... def next(self, *args): ... return self.gen.next() ... @staticmethod ... def nextargs(): ... return sys._getframe(2).f_locals['args'] ... py> def mygen(): ... while True: ... data, = gen.nextargs() ... print data ... yield None ... py> g = gen(mygen()) py> g.next(1) 1 py> g.next(2) 2
Of course, it's still a little magical, but I think I like it a little better because you can see, looking only at 'mygen', when 'data' is likely to change value...
Steve
[1] http://www.python.org/peps/pep-0288.html -- http://mail.python.org/mailman/listinfo/python-list