[Python-ideas] Possible PEP 380 tweak
Ron Adam
rrr at ronadam.com
Tue Oct 26 03:01:29 CEST 2010
On 10/25/2010 03:21 PM, Guido van Rossum wrote:
> On Mon, Oct 25, 2010 at 12:53 PM, Ron Adam<rrr at ronadam.com> wrote:
>> This is how my mind wants to write this.
>>
>> @consumer
>> def reduce_collector(func):
>> try:
>> value = yield # No value to yield here.
>> while True:
>> value = func((yield), value) # or here.
>> except YieldError:
>
> IIUC this works today if you substitute GeneratorExit and use
> c.close() instead of next(c) below. (I don't recall why I split it out
> into two different try/except blocks but it doesn't seem necessary.
I tried it, c.close() doesn't work yet, but it does work with
c.throw(GeneratorExit) :-) But that still uses yield to get the value.
I used a different way of starting the generator that checks for a value
being yielded.
class GeneratorStartError(TypeError): pass
def start(g):
value = next(g)
if value is not None:
raise GeneratorStartError('started generator yielded a value')
return g
def reduce_collector(func):
value = None
try:
value = yield
while True:
value = func((yield), value)
except GeneratorExit:
yield value
def parallel_reduce(iterable, funcs):
collectors = [start(reduce_collector(func)) for func in funcs]
for v in iterable:
for coll in collectors:
coll.send(v)
return [c.throw(GeneratorExit) for c in collectors]
def main():
it = range(100)
print(parallel_reduce(it, [min, max]))
if __name__ == '__main__':
main()
> As for being able to distinguish next(c) from c.send(None), that's a
> few language revisions too late. Perhaps more to the point, I don't
> like that idea; it breaks the general treatment of things that return
> None and throwing away values. (Long, long, long ago there were
> situations where Python balked when you threw away a non-None value.
> The feature was boohed off the island and it's better this way.)
I'm not sure I follow the relationship you suggest. No values would be
thrown away. Or did you mean that it should be ok to throw away values? I
don't think it would prevent that either.
What the YieldError case really does is give the generator a bit more
control. As far as the calling routine that uses it is concerned, it just
works. What happend inside the generator is completely transparent to the
routine using the generator. If the calling routine does see a YieldError,
it means it probably was a bug.
>> # next was called not send.
>> yield value
>
> I object to overloading yield for both a *resumable* operation and
> returning a (final) value; that's why PEP 380 will let you write
> "return value". (Many alternatives were considered but we always come
> back to the simple "return value".)
That works for me. I think lot of people will find it easy to learn.
>> def parallel_reduce(iterable, funcs):
>> collectors = [reduce_collector(func) for func in funcs]
>> for v in iterable:
>> for coll in collectors:
>> coll.send(v)
>> return [next(c) for c in collectors]
>
> I really object to using next() for both getting the return value and
> the next yielded value. Jacob's proposal to spell this as c.close()
> sounds much better to me.
If c.close also throws the GeneratorExit and returns a value, that would be
cool. Thanks.
I take it that the objections have more to do with style and coding
practices rather than what is possible.
>> It nicely separates input and output parts of a co-function, which can be
>> tricky to get right when you have to receive and send at the same yield.
>
> I don't think there was a problem with this in my code (or if there
> was you didn't solve it).
There wasn't in this code. This is one of those areas where it can be
really difficult to find the correct way to express a co-function that does
both input and output, but not necessarily in a fixed order.
I begin almost any co-function with this at the top of the loop and later
trim it up if parts of it aren't needed.
out_value = None
while True:
in_value = yield out_value
out_value = None
...
# rest of loop to check in_value and modify out_value
As long as None isn't a valid data item, this works most of the time.
>> Maybe in Python 4k? Oh well. :-)
>
> Nah.
I'm ok with that.
Ron
More information about the Python-ideas
mailing list