<div dir="ltr"><div class="gmail_extra">On Thu, May 10, 2018 at 5:17 AM, Nick Coghlan <span dir="ltr"><<a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>></span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span class="">On 9 May 2018 at 03:06, Guido van Rossum <span dir="ltr"><<a href="mailto:guido@python.org" target="_blank">guido@python.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>So the way I envision it is that *in the absence of a nonlocal or global declaration in the containing scope*, := inside a comprehension or genexpr causes the compiler to assign to a local in the containing scope, which is elevated to a cell (if it isn't already). If there is an explicit nonlocal or global declaration in the containing scope, that is honored.<br><br></div>Examples:<br><br></div><div>  # Simplest case, neither nonlocal nor global declaration<br></div>  def foo():<br></div>      [p := q for q in range(10)]  # Creates foo-local variable p<br></div>      print(p)  # Prints 9<br><br></div><div>  # There's a nonlocal declaration<br></div>  def bar():<br></div><div>      p = 42  # Needed to determine its scope<br></div>      def inner():<br></div>          nonlocal p<br></div>          [p := q for q in range(10)]  # Assigns to p in bar's scope<br></div>      inner()<br></div>      print(p)  # Prints 9<br><br></div><div>  # There's a global declaration<br></div>  def baz():<br></div><div>      global p<br></div>      [p := q for q in range(10)]<br></div>  baz()<br></div>  print(p)  # Prints 9<br><br></div>All these would work the same way if you wrote list(p := q for q in range(10)) instead of the comprehension.<br></div></blockquote><div><br></div></span><div>How would you expect this to work in cases where the generator expression isn't immediately consumed? If "p" is nonlocal (or global) by default, then that opens up the opportunity for it to be rebound between generator steps. That gets especially confusing if you have multiple generator expressions in the same scope iterating in parallel using the same binding target:<br><br></div><div>    # This is fine<br></div><div>    gen1 = (p for p in range(10))<br></div><div><div><div>    gen2 = (p for p in gen1)<br></div><div>    print(list(gen2))<br><br></div><div><div></div></div>    # This is not (given the "let's reintroduce leaking from comprehensions" proposal)<br></div><div>    p = 0<br></div><div>    gen1 = (p := q for q in range(10))<br></div><div><div><div>    gen2 = (p, p := q for q in gen1)<br></div><div></div></div></div><div>    print(list(gen2))<br></div></div></div></div></div></blockquote><div><br></div><div>That's just one of several "don't do that" situations. *What will happen* is perhaps hard to see at a glance, but it's perfectly well specified. Not all legal code does something useful though, and in this case the obvious advice should be to use different variables.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div></div>It also reintroduces the original problem that comprehension scopes solved, just in a slightly different form:<br><br></div><div>    # This is fine<br></div><div>    for x in range(10):<br></div><div>        for y in range(10):<br></div><div>            transposed_related_coords = [y, x for x, y in related_coords(x, y)]<br></div><div><br></div><div>    # This is not (given the "let's reintroduce leaking from comprehensions" proposal)<br></div><div>    for x in range(10):<br></div><div>        for y in range(10):<br></div><div>            related_interesting_coords = [x, y for x in related_x_coord(x, y) if is_interesting(y := f(x))]<br></div><div><br></div>Deliberately reintroducing stateful side effects into a nominally functional construct seems like a recipe for significant confusion, even if there are some cases where it might arguably be useful to folks that don't want to write a named function that returns multiple values instead.<br></div></div></div></blockquote><div><br></div><div>You should really read Tim's initial post in this thread, where he explains his motivation. It sounds like you're not buying it, but your example is just a case where the user is shooting themselves in the foot by reusing variable names. When writing `:=` you should always keep the scope of the variable in mind -- it's no different when using `:=` outside a comprehension.<br></div><div> </div></div></div><div class="gmail_extra">PS. Thanks for the suggestion about conflicting signals about scope; that's what we'll do.<br><div class="gmail_quote"><br></div></div><div class="gmail_extra">-- <br><div class="gmail_signature" data-smartmail="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div></div>