<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 25 March 2018 at 13:51, Chris Angelico <span dir="ltr"><<a href="mailto:rosuav@gmail.com" target="_blank">rosuav@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Sun, Mar 25, 2018 at 2:48 PM, Ethan Furman <<a href="mailto:ethan@stoneleaf.us">ethan@stoneleaf.us</a>> wrote:<br>
> On 03/24/2018 01:35 AM, Nick Coghlan wrote:<br>
><br>
>> In comprehensions and generator expressions, we'd need to explain why<br>
>> inline assignments in the outermost iterator<br>
>> expression leak but those in filter expressions, inner iterator<br>
>> expressions, and result expressions don't.<br>
><br>
><br>
> I don't understand -- could you give an example?<br>
><br>
<br>
</span>Let's suppose we have assignment expressions. I'm going to use "(expr<br>
as name)" syntax for this example.<br>
<br>
a = [(1 as b) for c in (d as e) if (2 as f)]<br>
<br>
Which of these six names is local to the comprehension and which can<br>
leak? Due to the requirements of class scope, 'd' must be looked up in<br>
the outer scope. That means that its corresponding 'as e' must also<br>
land in the outer scope. 'a', of course, is in the outer scope. The<br>
other four are local to the inner function that implements the<br>
comprehension.<br></blockquote><div><br></div><div>Adding qualifiers to the names to show which names would be resolved where:<br><br>    outer_a = [(1 as inner_b) for inner_c in (outer_d as outer_e) if (2 as inner_f)]<br></div><div>    print(outer_a, outer_d, outer_e) # Works<br></div><div>    print (inner_b, inner_c, inner_f) # Name error</div><div><br></div><div>In current Python,with the outer scope's name bindings being read-only inside a comprehension, this difference in scope resolution really only matters at class scope, and when using the two-namespace form of exec and eval (since method semantics result in that impacting which names the comprehension can see). At function scope, referenced names from the outer scope get resolved as closure references, while at module scope they get resolved as global references, so you have to look at the generated bytecode to see the differences.<br><br></div><div>That all changes once we start allowing name binding (in any form) at the level of expressions rather than statements: regardless of the nature of the outer scope, the exact structure of the implicit comprehension scope becomes relevant whenever you bind names inside the comprehension.<br><br></div><div>Now, the notable *distinction* here relative to the old iteration variable leakage is that doing a name binding in the outermost iterable expression can always be refactored to use an ordinary assignment statement instead:<br><br>    outer_e = outer_d<br><div>    outer_a = [(1 as inner_b) for inner_c in outer_d if (2 as inner_f)]<br></div><div>    print(outer_a, outer_d, outer_e) # Works<br></div><div>    print (inner_b, inner_c, inner_f) # Name error<br><br></div><div>This formulation also makes it clear that "outer_e" will only resolve inside the comprehension if "outer_d" already resolves.<br></div><div></div><div><br></div><div>This means that if we did go down the expression-level-assignments-are-just-regular-name-binding-operations path, then using the embedded assignment form in such cases could be discouraged as misleadingly ambiguous by style guides and linters, without being outright prohibited by the compiler itself.<br></div></div><div><br></div><div>Cheers,<br></div><div>Nick.<br></div></div><br>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>   |   Brisbane, Australia</div>
</div></div>