[Python-ideas] PEP 380 close and contextmanagers?
Ron Adam
rrr at ronadam.com
Wed Oct 27 18:18:58 CEST 2010
On 10/27/2010 10:01 AM, Ron Adam wrote:
>
> On 10/25/2010 10:25 PM, Guido van Rossum wrote:
>> By the way, here's how to emulate the value-returning-close() on a
>> generator, assuming the generator uses raise StopIteration(x) to mean
>> return x:
>>
>> def gclose(gen):
>> try:
>> gen.throw(GeneratorExit)
>> except StopIteration, err:
>> if err.args:
>> return err.args[0]
>> except GeneratorExit:
>> pass
>> return None
>>
>> I like this because it's fairly straightforward (except for the detail
>> of having to also catch GeneratorExit).
>>
>> In fact it would be a really simple change to gen_close() in
>> genobject.c -- the only change needed there would be to return
>> err.args[0]. I like small evolutionary improvements to APIs.
>
> Here's an interesting idea...
>
> It looks like a common case for consumer co-functions is they need to be
> started and then closed, so I'm wondering if we can make these work
> context managers? That may be a way to reduce the need for the
> try/except blocks inside the generators.
It looks like No context managers return values in the finally or __exit__
part of a context manager. Is there way to do that?
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)
# 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?
def reduce_i(f):
i = yield
while True:
i = f(i, (yield i))
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]
def main():
with consumer(reduce_it_to([min, max])) as c:
for i in range(100):
value = c.send(i)
print(value)
if __name__ == '__main__':
main()
More information about the Python-ideas
mailing list