Bjoern Schliessmann <[EMAIL PROTECTED]> writes: > The solution I'd use is a decorator that calls next automatically one > time after instantiation. Then you can use send normally, and don't > have to care about any initial parameters, which makes the code > clearer (initial parameters should be used for setup purposes, but not > for the first iteration, IMHO). It'd look like this (from PEP 342, > http://www.python.org/dev/peps/pep-0342/):
I'd seen the consumer decorator, and it certainly is cleaner than just using a generator. I don't like how it hides the parameter signature in the middle of the consumer function though, and it also doesn't provide for argument default values. It's the difference between: ... def __init__(self, ...): ... self.consumer = self._function(value) ... def function(self, first, second=3, *args, **kwargs): self.consumer.send((first, second, args, kwargs)) @consumer def _function(self, setup): ... first, second, args, kwargs = yield # initial 'next' while condition: ... first, second, args, kwargs = yield retval Versus just: @coroutine def function(self, first, second=3, *args, **kwargs): ... while condition: ... first, second, args, kwargs = yield retval Thanks in any case for the replies! Since I've apparently decided my ArgPacker is worth it, the complete code for my coroutine decorator follows. -Marshall import inspect import types import functools from itertools import izip __all__ = [ 'coroutine' ] class ArgPacker(object): def __init__(self, function): args, varargs, varkw, defaults = inspect.getargspec(function) self.args = args or [] self.varargs = (varargs is not None) and 1 or 0 self.varkw = (varkw is not None) and 1 or 0 self.nargs = len(self.args) + self.varargs + self.varkw defaults = defaults or [] defargs = self.args[len(self.args) - len(defaults):] self.defaults = dict([(k, v) for k, v in izip(defargs, defaults)]) def pack(self, *args, **kwargs): args = list(args) result = [None] * self.nargs for i, arg in izip(xrange(len(self.args)), self.args): if args: result[i] = args.pop(0) elif arg in kwargs: result[i] = kwargs[arg] del kwargs[arg] elif arg in self.defaults: result[i] = self.defaults[arg] else: return None if self.varargs: result[len(self.args)] = args elif args: return None if self.varkw: result[-1] = kwargs elif kwargs: return None return tuple(result) class coroutine(object): """Convert a function to be a simple coroutine. A simple coroutine is a generator bound to act as much as possible like a normal function. Callers call the function as usual while the coroutine produces new return values and receives new arguments with `yield'. """ def __init__(self, function): self.function = function self.gname = ''.join(['__', function.__name__, '_generator']) self.packer = ArgPacker(function) coroutine = self def method(self, *args, **kwargs): return coroutine.generate(self, self, *args, **kwargs) self.method = method functools.update_wrapper(self, function) functools.update_wrapper(method, function) def __get__(self, obj, objtype=None): return types.MethodType(self.method, obj, objtype) def __call__(self, *args, **kwargs): return self.generate(self, *args, **kwargs) def generate(self, obj, *args, **kwargs): try: generator = getattr(obj, self.gname) except AttributeError: generator = self.function(*args, **kwargs) setattr(obj, self.gname, generator) retval = generator.next() else: packed = self.packer.pack(*args, **kwargs) if packed is None: self.function(*args, **kwargs) # Should raise TypeError raise RuntimeError("ArgPacker reported spurious error") retval = generator.send(packed) return retval -- http://mail.python.org/mailman/listinfo/python-list