[Python-ideas] Possible PEP 380 tweak
Carl M. Johnson
cmjohnson.mailinglist at gmail.com
Fri Oct 29 05:17:14 CEST 2010
On Thu, Oct 28, 2010 at 4:21 PM, Guido van Rossum <guido at python.org> wrote:
> On Thu, Oct 28, 2010 at 6:17 PM, Nick Coghlan <ncoghlan at gmail.com> wrote:
>> To use a toy example:
>>
>> # Even this toy framework needs a little structure
>> class EndSum(Exception): pass
>>
>> def gsum():
>> # Sums sent values until EndSum or GeneratorExit are thrown in
>> tally = 0
>> try:
>> while 1:
>> tally += yield
>> except (EndSum, GeneratorExit):
>> pass
>> return x
>
> You meant return tally. Right?
>
>> def average_sums():
>> # Advances to a new sum when EndSum is thrown in
>> # Finishes the last sum and averages them all when GeneratorExit
>> is thrown in
>> sums = []
>> try:
>> while 1:
>> sums.append(yield from gsum())
>> except GeneratorExit as ex:
>> # Our proposed expansion tweak is to enable the next line
>> sums.append(ex.args[0])
>> return sum(sums) / len(sums)
>
This toy example is a little confusing to me because it has typos…
which is natural when one is writing a program without being able to
run it to debug it. So, I wrote a version of the accumulator/averager
that will work in Python 2.7 (and I think 3, but I didn't test it):
class ReturnValue(Exception): pass
def prime_pump(gen):
def f(*args, **kwargs):
g = gen(*args, **kwargs)
next(g)
return g
return f
@prime_pump
def accumulator():
total = 0
length = 0
try:
while 1:
value = yield
total += value
length += 1
print(length, value, total)
except GeneratorExit:
r = ReturnValue()
r.total = total
r.length = length
raise r
@contextmanager
def get_sum(it):
try:
it.close()
except ReturnValue as r:
yield r.total
@contextmanager
def get_average(it):
try:
it.close()
except ReturnValue as r:
yield r.total / r.length
def main():
running_total = accumulator()
sums = accumulator()
running_total.send(6) #For example, whatever
running_total.send(7)
with get_sum(running_total) as first_sum:
sums.send(first_sum)
running_total = accumulator() #Zero it out
running_total.send(2) #For example, whatever
running_total.send(2)
running_total.send(5)
running_total.send(8)
with get_sum(running_total) as second_sum:
sums.send(second_sum)
#Get the average of the sums
with get_average(sums) as r:
return r
main()
So, I guess the question I have is how will the proposed extensions to
the language make the above code prettier? One thing I can see is that
if it's possible to return from inside a generator, it can be more
straightforward to get the values out of the accumulator at the end:
try:
while 1:
value = yield
total += value
length += 1
print(length, value, total)
except GeneratorExit:
return total, length
With Guido's proposed "for item from yield" syntax, IIUC this can be
prettied up even more as:
for value from yield:
total += value
length += 1
return total, length
Are there other benefits to the proposed extensions? How will the call
sites be improved? I'm not sure how I would rewrite main() to be
prettier/more clear in light of the proposals…
Thanks,
-- Carl Johnson
More information about the Python-ideas
mailing list