[Python-Dev] accumulator display syntax

Tim Peters tim_one at email.msn.com
Fri Oct 24 23:25:48 EDT 2003


[Tim]
>    squares = (f(x)**2 for x in inputs)  # assuming reiterability here
> ...
>    for f in math.sin, math.cos, math.tan:
>        plot(squares)

[Skip Montanaro]
> How much more expensive

Stop right there.  I must have been unclear.  The only point of the example
was semantic, not cost:  even if generator expressions used closure
semantics, the example *still* wouldn't work the way it appears to read, and
because generator expressions aren't reiterable.  What the example would do
under closure semantics:

1. Plot the square of math.sin(x), for each x in inputs.

then

2. Probably nothing more than that.  The "squares" GE is exhausted after
   #1 completes, and no matter often it's run again it's simply going to
   raise StopIteration at once each time it's tried.  A reasonable plot()
   would probably do nothing when fed an exhausted iterable, but maybe it
   would raise an exception.  That's up to plot().  What it *won't* do
   under any scheme here is go on to plot the squares of math.cos(x) and
   math.tan(x) over the inputs too.

The lack of reiterability (which is fine by me!) thus seems to make a
plausible use for closure semantics hard to imagine.  The example was one
where closure semantics suck despite misleading appearance.

Closures are very often used (in languages other than Python, and in Python
too by people who haven't yet learned to write Python <0.9 wink>) to hold
mutable state in outer scopes, for the use of functions in inner scopes,
very much like an instance's data attributes hold mutable state for the use
of methods defined in the instance's class.  In those common cases, the
power comes from being able to run functions (methods) more than once, or to
reuse the mutable state among functions (methods).  But generator
expressions are always one-shot computations (you get to run a GE to
completion no more than once).  There may be some use for closure semantics
in a collection of GEs that reference each other (similar to instance data
being visible to multiple methods), but so far I've failed to dream up a
plausible case of that nature either.

> would this be than
>
>     for f in math.sin, math.cos, math.tan:
>         squares = (f(x)**2 for x in inputs)
>         plot(squares)

Despite the similar appearance, that does something very different, plotting
all 3 functions (not just math.sin), and regardless of whether closure or
capture semantics are used.  I expect the body of the loop in real life
would be a one-liner, though:

        plot(f(x)**2 for x in inputs)

> which would work without reiterability, right?

Yup.

> The underlying generator function could still be created at compile-time
> and it (or its code object?) stored in the current function's constants.
> 'f' is simply an argument to it when the iterator is instantiated.

Guido expanded on that already.  The code is compiled only once (at "compile
time"), and there's a small runtime cost per outer-loop iteration to build a
function object from the (pre-compiled) code object, and a possibly larger
runtime cost per outer-loop iteration to start the GE.  Passing 'f' and
'inputs' may be part of either of those costs, depending on how it's
implemented -- but giving the synthesized generator function some
initialized locals is the least of the runtime costs.




More information about the Python-Dev mailing list