[Python-Dev] PEP 334 - Simple Coroutines via SuspendIteration

Michael Sparks zathras at thwackety.com
Fri Oct 1 00:36:02 CEST 2004


On Thu, 30 Sep 2004, Phillip J. Eby wrote:
...
> A mechanism to pass values or exceptions into generators

[ Possibly somewhat off topic, and apologies if it is, and I'm positive
  someone's done something similar before, but I think it's relevant to
  the discussion in hand -- largely because the above use case *doesn't*
  require changes to python... ]

A minimal(ish) example of a technique for doing this I presented last week
at a streaming workshop looked like the following. (amongst other stuff...)

Create a decorator that wraps the generator inside a class, derived from a
supplied base class. (default to object)

import copy
def wrapgenerator(bases=object, **attrs):
   def decorate(func):
       class statefulgenerator(bases):
          __doc__ = func.__doc__
          def __init__(self,*args):
             super(statefulgenerator, self).__init__(*args)
             self.func=func(self,*args)
             for k in attrs.keys(): self.__dict__[k] = copy.deepcopy(attrs[k])
             self.next=self.__iter__().next
          def __iter__(self): return iter(self.func)
       return statefulgenerator
   return decorate

Create a class to handle the behaviour you wish to use to communicate with
the generator from outside:

class component(object):
   def __init__(self, *args):
       # Default queues
       self.queues = {"inbox":[],"control":[],"outbox":[],"signal":[]}
   def send(self, box, object): self.queues[box].append(object)
   def dataReady(self,box): return len(self.queues[box])>0
   def recv(self, box): # NB. Exceptions aren't caught
      X=self.queues[box][0]
      del self.queues[box][0]
      return X

Then just use something like this:

@wrapgenerator(component)
def forwarderNay(self):
   "Simple data forwarding generator"
   while 1:
      if self.dataReady("inbox"):
         self.send("outbox",self.recv("inbox")+"Nay")
      elif self.dataReady("control"):
         if self.recv("control") == "shutdown":
            break
      yield 1
   self.send("signal","shutdown")
   yield 0

In conjuction with a simplistic scheduler, and linkage functions this
allows you to have something similar to CSP. I've come to the conclusion
recently that the fact you can't* yield across multiple levels is actually
beneficial because it encourages you to use many small components.

Used standalone you can do this though:

f=forwarderNay()
for word in ["hello", "world", "test"]:
   f.send("inbox", word)
   f.next()
   print f.recv("outbox"),
print

Which of course outputs:
   * helloNay worldNay testNay

For small scale things this amount of cruft is a bit of a pain. We're
using *essentially* this approach to build network servers and it seems a
rather satisfying way of doing so TBH. (not exactly this approach so if
this looks odd, that's why - I'm not allowed to release the source for
precisely what we're doing :-( but the above I was on the slides I *was*
able to talk about... :-)

Hope that's of some use...

Best Regards,


Michael.




More information about the Python-Dev mailing list