[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Python-Dev] PEP 572 semantics

On Thu, Jul 5, 2018 at 3:35 PM Nick Coghlan <ncoghlan at gmail.com> wrote:

> Guido did fully specify this in his post on "__parentlocal" scoping, in
> response to my request that this be clearly spelled out in the PEP (that
> specification just hasn't been rolled back into the PEP yet).

Having written it up that way, I don't think it actually would add clarity
to the PEP. What would probably add clarity is some examples showing the
equivalent "classic" code for the various edge cases involving
comprehensions, showing where the "nonlocal" or "global" would go.

> While Tim's correct that the underlying runtime semantics here aren't new
> (which is why PEP 558 doesn't need to change), there's a new requirement
> imposed on a Python compiler's symbol table analysis pass to see "A :=
> expr" in a comprehension or generator expression scope

(To clarify: as always, this scope excludes the "outermost iterable".)

> and interpret that as equivalent to either:
> 1. An implied "global A" in the child scope if the parent scope is the
> module scope, or A is explicitly declared as global in the parent scope

Yes. Two examples (so I can copy them easily into the PEP):

# Module scope
x = [a := i for i in range(10)]

# Translates roughly into
def comprehension(iterator):
    global a
    result = []
    for i in iterator:
        result.append(a := i)  # The meaning of this := should be clear
    return result
x = comprehension(iter(range(10)))

# Explicit global declaration
def foo():
    global a
    x = [a := i for i in range(10)]

# The translation is identical to the above except nested inside foo()

2. An implied "nonlocal A" in the child scope if the parent scope is a
> function scope and A is not defined as global in that scope. If A is not
> already declared as local or nonlocal in the parent scope, then it is
> implicitly declared as local in that scope with no associated annotation
> (akin to "if 0: for A in (): pass")

I'm confused why you use both "if 0:" and "for A in ():" here -- I'd think
that you can use either "if 0: A = 0" or "for A in (): pass" (the latter
without the "if 0:" prefix). My personal favorite here is currently "A:
object" -- since PEP 526 this already means "A is local in this function"
without giving it a value. But any of the others will work too.

Anyway you're right there are three subcases here:

- explicit nonlocal in the parent scope
- variable is already assigned to or declared in the parent scope (at least
one of "A = ..." or "A: ...")
- neither

Only the third subcase requires adding a dummy local declaration (i.e., "if
0: A = 0" or one of the others discussed above). (Note that all three
subcases add "nonlocal A" to the implied function).

There's also another special case when one comprehension occurs inside
another comprehension, e.g.

[[A := i+j for i in range(3)] for j in range(5)]

In this case the function generated for *both* comprehensions needs to
contain "nonlocal A" (or, if case (1) above applies, both should "global
A"). I believe this case is currently not mentioned in the PEP, though I
recall mentioning it before in one of the many threads. I've made a mental
note of this.

The prohibition on assignment to loop control variables applies to all loop
control variables that are visible from the point of the ":=" operator: in
this example, the variable at position A is not allowed to reference either
i or j. I would also disallow assignment to the loop control variable in
the "outer iterable" of a comprehension, since I don't think there's any
point in allowing that: [i for i in i := range(3)] should be disallowed.
All such prohibitions should be syntax errors.

> 3. A compile time error if the parent scope is a class scope (since we
> don't have any existing scope declaration semantics that can be used to
> make that case work sensibly)


> I'm still wondering if it might make sense to define a new
> "TargetScopeError" subclass of SyntaxError for that last case, since it
> isn't the assignment expression syntax itself that's the problem: it's
> where that expression is located.

Yeah, that would be a good idea. (Though do we currently have any
subclasses of SyntaxError? And if so, do any of them have a name that does
not include the substring "SyntaxError"?)

--Guido van Rossum (python.org/~guido)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20180705/0450c744/attachment.html>