<div dir="ltr"><div class="gmail_quote"><div>On Thu, Jul 5, 2018 at 3:35 PM Nick Coghlan <<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>> wrote: <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="auto">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).</div></div></blockquote><div><br></div><div>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.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="auto">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</div></div></blockquote><div><br></div><div>(To clarify: as always, this scope excludes the "outermost iterable".)<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="auto"> and interpret that as equivalent to either:</div><div dir="auto"><br></div><div dir="auto">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</div></div></blockquote><div><br></div><div>Yes. Two examples (so I can copy them easily into the PEP):</div><div><br></div><div># Module scope</div><div>x = [a := i for i in range(10)]</div><div><br></div><div># Translates roughly into</div><div>def comprehension(iterator):</div><div>    global a<br></div><div>    result = []</div><div>    for i in iterator:</div><div>        result.append(a := i)  # The meaning of this := should be clear<br></div><div>    return result</div><div>x = comprehension(iter(range(10)))</div><div><br></div><div># Explicit global declaration</div><div>def foo():</div><div>    global a</div><div>    x = [a := i for i in range(10)]</div><div><br></div><div># The translation is identical to the above except nested inside foo()<br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="auto">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")</div></div></blockquote><div><br></div><div>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.</div><div><br></div><div>Anyway you're right there are three subcases here:</div><div><br></div><div>- explicit nonlocal in the parent scope</div><div>- variable is already assigned to or declared in the parent scope (at least one of "A = ..." or "A: ...")</div><div>- neither</div><div><br></div><div>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).<br></div><div><br></div><div>There's also another special case when one comprehension occurs inside another comprehension, e.g.</div><div><br></div><div>[[A := i+j for i in range(3)] for j in range(5)]</div><div><br></div><div>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.<br></div><div><br></div><div>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.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="auto">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)</div></div></blockquote><div><br></div><div>Right. <br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="auto"></div><div dir="auto">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.</div></div></blockquote><div><br></div><div>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"?)<br></div></div><br>-- <br><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido">python.org/~guido</a>)</div></div>