[Python-ideas] Possible PEP 380 tweak (original) (raw)

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 averagesums():  # 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