[Python-Dev] accumulator display syntax

Tim Peters tim_one at email.msn.com
Wed Oct 22 22:07:48 EDT 2003


[Tim]
>> I'm not sure it's "a feature" that
>>
>>     print [n+f() for x in range(10)]
>>
>> looks up n and f anew on each iteration -- if I saw a listcomp that
>> actually relied on this, I'd be eager to avoid inheriting any of
>> author's code.

[Guido]
> It's just a direct consequence of Python's general rule for name
> lookup in all contexts: variables are looked up when used, not before.
> (Note: lookup is different from scope determination, which is done
> mostly at compile time.  Scope determination tells you where to look;
> lookup gives you the actual value of that location.)  If n is a global
> and calling f() changes n, f()+n differs from n+f(), and both are
> well-defined due to the left-to-right rule.  That's not good or bad,
> that's just *how it is*.  Despite having some downsides, the
> simplicity of the rule is good; I'm sure we could come up with
> downsides of other rules too.

Sorry, but none of that follows unless you first insist that a listcomp is
semantically equivalent to a particular for-loop.  Which we did do at the
start, and which is now being abandoned in part ("well, except for the for
target(s) -- well, OK, they still work like exactly like the for-loop would
work if the target(s) were renamed in a particular 'safe' way").  I don't
mind the renaming trick there, but by the same token there's nothing to stop
explaining the meaning of a generator expression as a particular way of
writing a generator function either.  It's hardly a conceptual strain to
give the function default arguments, or even to eschew that technical
implementation trick and just say the generator's frame gets some particular
initialized local variables (which is the important bit, not the trick used
to get there).

> Despite the good case that's been made for what would be most useful,

I don't see that any good case had been made for or against it:  the only
cases I care about are real use cases.  A thing stands or falls by that,
purity be damned.  I have since posted the first plausible use case that
occurred to me while thinking about real work, and "closure semantics"
turned out to be disastrous in that example (see other email), while
"capture the current binding" semantics turned out to be exactly right in
that example.  I suspected that would be so, but I still want to see more
not-100%-fabricated examples.

> I'm loathe to drop the evaluation rule for convenience in one special
> case.  Next people may argue that in Python 3.0 lambda should also do
> this; arguably it's more useful than the current semantics there too.

It's not analogous:  when I'm writing a lambda, I can *choose* which
bindings to capture at lambda definition time, and which to leave free.
Unless generator expressions grow more hair, I have no choice when writing
one of those, so the implementation-forced choice had better be
overwhelmingly most useful most often.  I can't judge the latter without
plausible use cases, though.

> And then what next -- maybe all nested functions should copy their
> free variables?

Same objection as to the lambda example.

> Oh, and then maybe outermost functions should copy their globals into
> locals too -- that will speed up a lot of code. :-)

It would save Jim a lot of thing=thing arglist typing in Zope code too
<wink>.

> There are other places in Python where some rule is applied to "all
> free variables of a given piece of code" (the distinction between
> locals and non-locals in functions is made this way).  But there are
> no other places where implicit local *copies* of all those free
> variables are taken.

I didn't suggest to copy anything, just to capture the bindings in use at
the time a generator expression is evaluated.  This is easy to explain, and
trivial to explain for people familiar with the default-argument trick.
Whenever I've written a list-of-generators, or in the recent example a
generator pipeline, I have found it semantically necessary, without
exception so far, to capture the bindings of the variables whose bindings
wouldn't otherwise be invariant across the life of the generator.  It it
turns out that this is always, or nearly almost always, the case, across
future examples too, then it would just be goofy not to implement generator
expressions that way ("well, yes, the implementation does do a wrong thing
in every example we had, but what you're not seeing is that the explanation
would have been a line longer had the implementation done a useful thing
instead" <wink>).

> I'd need to find a unifying principle to warrant doing that beyond
> utility.

No you don't -- you just think you do <wink>.




More information about the Python-Dev mailing list