http://www.python.org/dev/peps/pep-0380/ proposes new syntax ("yield from") for generators to delegate control to a "subgenerator" (really to any iterator). Any send/next/throw/close calls to the delegating generator are forwarded to the delegee, until the delegee is exhausted.

This is being considered for inclusion in Python 2.7, but I wanted a way to play around with the design pattern now (and in case the PEP isn't soon accepted, and on older Python installations regardless of what happens with future versions of Python).

So I came up with this decorator-based solution. The "supergenerator" decorator wraps the delegating generator with a control handler that takes care of directing send/next/throw/close calls to the delegator or delegee, as appropriate.

Sample usage is described in the decorator's docstring.

Delegees can pass return values to the "yield _from" call by "raise StopIteration(retval)". Example:

@supergenerator def gen1funct(): for i in xrange(3): sent = yield i print "sent1: %r" % (sent,) delegee = gen2funct() retval = yield _from(delegee) print "return value: %r" % (retval,) def gen2funct(): for i in xrange(3,6): sent = yield i print "sent2: %r" % (sent,) raise StopIteration(100) gen=gen1funct() try: i = gen.next() while True: print "yielded: %r" % (i,) i = gen.send(i*10) except StopIteration: print "yielded: %r" % (i,) print "stopped"

Result:

yielded: 0 sent1: 0 yielded: 1 sent1: 10 yielded: 2 sent1: 20 yielded: 3 sent2: 30 yielded: 4 sent2: 40 yielded: 5 sent2: 50 return value: 100 yielded: 5 stopped

This is the "simple" version of my implementation. It doesn't do any special handling of nested "yield _from"s. As a result, there's an extra level of generator-switching overhead for each delegating generator. If there are many levels of nesting, this will add up.

However, I also have an "optimized" implementation which automatically keeps track of nested "yield _from"s, and delegates directly to the most deeply-nested delegees. See http://code.activestate.com/recipes/576728/.

The flow control in this simple implementation is less straightforward than it might be. I did that in order to maximize the similarity between the simple and optimized versions.