[Python-ideas] PEP 380 close and contextmanagers? (original) (raw)

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, *excinfo):  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, *excinfo):  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().

def reducei(f): i = yield while True: i = f(i, (yield i))

Unfortunately from here on till the end of your example my brain exploded.

def reduceitto(funcs):  with multiconsumer([reducei(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(reduceitto([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