[Python-ideas] Possible PEP 380 tweak

Guido van Rossum guido at python.org
Fri Oct 29 21:13:18 CEST 2010


On Fri, Oct 29, 2010 at 12:18 AM, Greg Ewing
<greg.ewing at canterbury.ac.nz> wrote:
> I've been pondering the whole close()-returning-a-value
> thing I've convinced myself once again that it's a bad
> idea.
>
> Essentially the problem is that we're trying to make
> the close() method, and consequently GeneratorExit,
> serve two different and incompatible roles.
>
> One role (the one it currently serves) is as an
> emergency bail-out mechanism. In that role, when we
> have a stack of generators delegating via yield-from,
> we want things to behave as thought the GeneratorExit
> originates in the innermost one and propagates back
> out of the entire stack. We don't want any of the
> intermediate generators to catch it and turn it
> into a StopIteration, because that would give the
> next outer one the misleading impression that it's
> business as usual, but it's not.

This seems to be the crux of your objection. But if I look carefully
at the expansion in the current version of PEP 380, I don't think this
problem actually happens: If the outer generator catches
GeneratorExit, it closes the inner generator (by calling its close
method, if it exists) and then re-raises the GeneratorExit:

            except GeneratorExit as _e:
                try:
                    _m = _i.close
                except AttributeError:
                    pass
                else:
                    _m()
                raise _e

I would leave this expansion alone even if g.close() was changed to
return the generator's return value.

Could it be that you are thinking of your accelerated implementation,
which IIRC has a shortcut whereby generator operations (next, send,
throw) on the outer generator are *directly* passed to the inner
generator when a yield-from is active?

It looks to me as if using g.close() to capture the return value of a
generator is not of much value when using yield-from, but it can be of
value for the simpler pattern that started this thread. Here's an
updated version:

def gclose(gen):  ## Not needed with PEP 380
  try:
    gen.throw(GeneratorExit)
  except StopIteration as err:
    return err.args[0]
  except GeneratorExit:
    pass
  # Note: other exceptions are passed out untouched.
  return None

def summer():
  total = 0
  try:
    while True:
      total += yield
  except GeneratorExit:
    raise StopIteration(total)  ## return total

def maxer():
  highest = 0
  try:
    while True:
      value = yield
      highest = max(highest, value)
  except GeneratorExit:
    raise StopIteration(highest)  ## return highest

def map_to_multiple(it, funcs):
  gens = [func() for func in funcs]  # Create generators
  for gen in gens:
    next(gen)  # Prime generators
  for value in it:
    for gen in gens:
      gen.send(value)
  return [gclose(gen) for gen in gens]  ## [gen.close() for gen in gens]

def main():
  print(map_to_multiple(range(100), [summer, maxer]))

main()

-- 
--Guido van Rossum (python.org/~guido)



More information about the Python-ideas mailing list