#### The recipe from __future__ import generators from inspect import getargspec, formatargspec _redefinition = """ _redef_tmp = %(name)s def %(name)s%(oldargs)s: wrapped = type('_GeneratorWrapper', (object,), %(name)s._realgen.__dict__)() wrapped.__iter__ = lambda self: self wrapped.next = %(name)s._realgen%(newargs)s.next return wrapped %(name)s.__doc__ = _redef_tmp.__doc__ %(name)s._realgen = _redef_tmp del _redef_tmp """ def enableAttributes(genfunc): """Wrapper for generators to enable classlike attribute access. The generator definition should specify 'self' as the first parameter. Calls to a wrapped generator should ignore the self parameter. """ old = getargspec(genfunc) old[0].pop(0) new = getargspec(genfunc) new[0][0] = 'wrapped' specs = {'name': genfunc.func_name, 'oldargs': formatargspec(*old), 'newargs': formatargspec(*new)} exec(_redefinition % specs, genfunc.func_globals) #### A minimal, complete example def outputCaps(self, logfile): """Convert to uppercase and emit to stdout and logfile""" self.lineno = 1 while True: logfile.write(self.line.upper()) print self.prefix, self.line.upper(), yield None self.lineno += 1 outputCaps.prefix = 'Capitalized:' # Make a class var style default enableAttributes(outputCaps) # Wrap the generator in a class g = outputCaps(open('destfil.txt','w')) for line in open('sourcefil.txt'): g.line = line.rstrip() # Data can be passed into the generator g.next() print g.lineno # Generators can also update the attributes print dir(g) # Still has __iter__() and next() print outputCaps.__doc__ # Docstrings survive wrapping print g.prefix # Gen attributes carry through to instances help(outputCaps) # PyDoc produces an accurate help screen