[Python-ideas] PEP 380 close and contextmanagers?
Guido van Rossum
guido at python.org
Wed Oct 27 20:38:49 CEST 2010
On Wed, Oct 27, 2010 at 9:18 AM, Ron Adam <rrr at ronadam.com> wrote:
>
>
> 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)
More information about the Python-ideas
mailing list