On Wed, Oct 27, 2010 at 9:18 AM, Ron Adam
On 10/27/2010 10:01 AM, Ron Adam wrote: It looks like No context managers return values in the finally or __exit__ part of a context manager. Is there way to do that?
How would that value be communicated to the code containing the with-clause?
Here's a context manager version of the min/max with nested coroutines, but it doesn't return a value from close.
###### from contextlib import contextmanager
# New close function that enables returning a # value.
def gclose(gen): try: gen.throw(GeneratorExit) except StopIteration as err: if err.args: return err.args[0] except GeneratorExit: pass return None
# Showing both the class and geneator based # context managers for comparison and to better # see how these things may work.
class Consumer: def __init__(self, cofunc): next(cofunc) self.cofunc = cofunc def __enter__(self): return self.cofunc def __exit__(self, *exc_info): gclose(self.cofunc)
@contextmanager def consumer(cofunc): next(cofunc) try: yield cofunc finally: gclose(cofunc)
class MultiConsumer: def __init__(self, cofuncs): for c in cofuncs: next(c) self.cofuncs = cofuncs def __enter__(self): return self.cofuncs def __exit__(self, *exc_info): for c in self.cofuncs: gclose(c)
@contextmanager def multiconsumer(cofuncs): for c in cofuncs: next(c) try: yield cofuncs finally: for c in cofuncs: gclose(c)
So far so good.
# Min/max coroutine example slpit into # nested coroutines for testing these ideas # in a more complex situation that may arise # when working with cofunctions and generators.
# Question: # How to rewrite this so close returns # a final value?
Change the function to catch GeneratorExit and when it catches that, raise StopIteration(<returnvalue>).
def reduce_i(f): i = yield while True: i = f(i, (yield i))
Unfortunately from here on till the end of your example my brain exploded.
def reduce_it_to(funcs): with multiconsumer([reduce_i(f) for f in funcs]) as mc: values = None while True: i = yield values values = [c.send(i) for c in mc]
Maybe you could have picked a better name than 'i' for this variable...
def main(): with consumer(reduce_it_to([min, max])) as c: for i in range(100): value = c.send(i) print(value)
I sort of get what you are doing here but I think you left one abstraction out. Something like this: def blah(it, funcs): with consumer(reduce_it_to(funcs) as c: for i in it: value = c.send(i) return value def main(): print(blah(range(100), [min, max]))
if __name__ == '__main__': main()
-- --Guido van Rossum (python.org/~guido)