On 6/28/2018 11:21 PM, Tim Peters wrote: [somewhere below] this is the last time I'm going to repeat it all again ;-) For me, this is your most convincing exposition and summary of why the proposal is at least ok. Thank you. > [Chris] > > yes, it was a contrived example, but the simplest one I could think > of off > > the top of my head that re-bound a name in the loop -- which was what I > > thought was the entire point of this discussion? > > But why off the top of your head? There are literally hundreds & > hundreds of prior messages about this PEP, not to mention that you could > also find examples in the PEP. Why make up a senseless example? > > > If we think hardly anyone is ever going to do that -- then I guess it > doesn't matter > > how it's handled. > > So look at real examples. One that's been repeated at least a hundred > times wants a local to "leak into" a listcomp: > > total = 0 > cumsums = [total ::= total + value for value in data] > > As an educator, how are you going to explain that blowing up with > UnboundLocalError instead? Do you currently teach that comprehensions > and genexps are implemented via invisible magically generated lexically > nested functions? If not, you're going to have to start for people to > even begin to make sense of UnboundLocalError if `total` _doesn't_ "leak > into" that example. My belief is that just about everyone who doesn't > know "too much" about the current implementation will be astonished & > baffled if that example doesn't "just work". > > In other cases it's desired that targets "leak out": > > while any(n % (divisor := p) == 0 for p in small_primes): > n //= divisor > > And in still other cases no leaking (neither in nor out) is desired. > > Same as `for` targets in that way,. but in the opposite direction: they > don't leak and there's no way to make them leak, not even when that's > wanted. Which _is_ wanted in the last example above, which would be > clearer still written as: > > while any(n % p == 0 for p in small_primes): > n //= p > > But that ship has sailed. > > > > ... > > And "nonlocal" is not used that often, and when it is it's for > careful closure > > trickery -- I'm guessing := will be far more common. > > My guess (recorded in the PEP's Appendix A) is that assignment > expressions _overall_ will be used more often than ternary `if` but > significantly less often than augmented assignment. I expect their use > in genexps and comprehensions will be minimal. There are real use cases > for them, but the vast majority of genexps and comprehensions apparently > have no use for them at all. > > > > And, of course, when a newbie encounters it, they can google it and > see what > > it means -- far different that seeing a := in a comprehension and > understanding > > (by osmosis??) that it might make changes in the local scope. > > Which relates to the above: how do you teach these things? The idea > that "a newbie" even _suspects_ that genexps and listcomps have > something to do with lexically nested scopes and invisible nested > functions strikes me as hilarious ;-) > > Regardless of how assignment expressions work in listcomps and genexps, > this example (which uses neither) _will_ rebind the containing block's `x`: > > [x := 1] > > How then are you going to explain that this seemingly trivial variation > _doesn't_? > > [x := 1 for ignore in "a"] > > For all the world they both appear to be binding `x` in the code block > containing the brackets. So let them. > > Even worse, > > [x for ignore in range(x := 1)] > > will rebind `x` in the containing block _regardless_ of how assignment > expression targets are treated in "most of" a comprehension, because the > expression defining the iterable of the outermost "for" _is_ evaluated > in the containing block (it is _not_ evaluated in the scope of the > synthetic function). > > That's not a special case for targets if they all "leak", but is if they > don't. > > > > And I don't think you can even do that with generator expressions > now -- as > > they can only contain expressions. > > Expressions can invoke arbitrary functions, which in turn can do > anything whatsoever. > > > Which is my point -- this would allow the local namespace to be > manipulated > > in places it never could before. > > As above, not true. However, it would make it _easier_ to write > senseless code mucking with the local namespace - if that's what you > want to do. > > > > Maybe it's only comprehensions, and maybe it'll be rare to have a > confusing > > version of those, so it'll be no big deal, but this thread started > talking about > > educators' take on this -- and as an educator, I think this really does > > complicate the language. > > I'll grant that it certainly doesn't simplify the language ;-) > > > > Python got much of it's "fame" by being "executable pseudo code" -- > its been > > moving farther and farther away from those roots. That's generally a > good thing, > > as we've gain expressiveness in exchangel, but we shouldn't pretend > it isn't > > happening, or that this proposal doesn't contribute to that trend. > > I didn't say a word about that one way or the other. I mostly agree, > but at the start Guido was aiming to fill a niche between shell > scripting languages and C. It was a very "clean" language from the > start, but not aimed at beginners. Thanks to his experience working on > ABC, it carried over some key ideas that were beginner-friendly, though. > > I view assignment expressions as being aimed at much the same audience > as augmented assignments: experienced programmers who already know the > pros and cons from vast experience with them in a large number of other > widely used languages. That's also a key Python audience. > > > ... > > Well, I've been surprised by what confused students before, and I > will again. But I > > dont hink there is any doubt that Python 3.7 is a notably harder to > learn that > > Python 1.5 was... > > Absolutely. It doesn't much bother me, though - at this point the > language and its widely used libraries are so sprawling that I doubt > anyone is fluent in all of it. That's a sign of worldly success. > > > ... > > and this: > > > > In [55]: x = 0 > > In [56]: [x for x in range(3)] > > Out[56]: [0, 1, 2] > >In [57]: x > > Out[57]: 0 > > > > doesn't change x in the local scope -- > > In Python 3, yes; in Python 2 it rebinds `x` to 2. > > > if that was a good idea, why is a good idea > > to have := in a comprehension effect the local scope?? > > Because you can't write a genexp or comprehension AT ALL without > specifying `for` targets, and in the overwhelming majority of genexps > and comprehensions anyone ever looked at, "leaking" of for-targets was > not wanted. "So don't let them leak" was pretty much a no-brainer for > Python 3. > > But assignment expressions are NEVER required to write a genexp or > comprehension, and there are only a handful of patterns known so far in > which assignment expressions appear to be of real value in those > contexts. In at least half those patterns, leaking _is_ wanted - > indeed, essential. In the rest, leaking isn't. > > So it goes. Also don't ignore other examples given before, showing how > having assignment expressions _at all_ argues for "leaking" in order to > be consistent with what assignment expressions do outside of > comprehensions and genexps. > > > > But maybe it is just me. > > Nope. But it has been discussed so often before this is the last time > I'm going to repeat it all again ;-) > > ... > > One of these conversations was started with an example something like > this: > > >> [(f(x), g(f(x))) for x in an_iterable] > > > > The OP didn't like having to call f() twice. So that would become: > > >> [ (temp:=f(x), g(temp)) for x in an_iterable] > > > > so now the question is: should "temp" be created / changed in the > enclosing local scope? > > > > This sure looks a lot like letting the iteration name (x in this > example) leak out - > > so I'd say no. > > In that example, right, leaking `temp` almost certainly isn't wanted. > So it goes. > > > > And I don't think this kind of thing would be rare. > > I do. It's dead easy to make up examples to "prove" anything people > like, but I'm unswayed unless examples come from real code, or are > obviously compelling. > > Since we're not going to get a way to explicitly say which targets > (neither `for` nor assignment expression) do and don't leak, it's a > reasonably satisfying compromise to say that one kind never leaks and > the other kind always leaks. The pick your poison accordingly. > > In the example above, note that they _could_ already do, e.g., > > [(fx, g(fx)) for x in an_iterable for fx in [f(x)]] > > Then nothing leaks (well, unless f() or g() do tricky things). I > personally wouldn't care that `temp` leaks - but then I probably would > have written that example as the shorter (& clearer to my eyes): > > [(v. g(v)) for v in map(f, an_iterable)] > > to begin with. -- Terry Jan Reedy
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4