
On Wed, Apr 18, 2018 at 10:13:58AM +1000, Chris Angelico wrote: [regarding comprehensions]
The changes here are only to edge and corner cases, other than as they specifically relate to assignment expressions. The current behaviour is intended to "do the right thing" according to people's expectations, and it largely does so; those cases are not changing. For list comprehensions at global or function scope, the ONLY case that can change (to my knowledge) is where you reuse a variable name:
[t for t in t.__parameters__ if t not in tvars]
This works in 3.7 but will fail easily and noisily (UnboundLocalError) with PEP 572.
That's a major semantic change, and the code you show is no better or worse than: t = ... result = [] for t in t.parameters: if t not in tvars: result.append(t) which is allowed. I think you need a better justification for breaking it than merely the opinion:
IMO this is a poor way to write a loop,
Reusing names is permitted in Python. If you're going to break code, that surely needs a future import or deprecation period. As you say yourself, you've already found one example in the standard library that will break.
and the fact that it "happened to work" is on par with code that depended on dict iteration order in Python 3.2 and earlier.
I don't think that's justified. As far as I can tell, the fact that it works is not a mere accident of implementation but a consequence of the promised semantics of comprehensions and Python's scoping rules. If that's not the case, I think you need to justify exactly why it isn't guaranteed.
Yes, the implementation is well defined, but since you can achieve exactly the same thing by picking a different variable name, it's better to be clear.
Ah, but the aim of the PEP is not to prohibit ugly or unclear code.
Note that the second of the open questions would actually return this to current behaviour, by importing the name 't' into the local scope.
Indeed. Maybe this needs to stop being an open question and become a settled question.
The biggest semantic change is to the way names are looked up at class scope. Currently, the behaviour is somewhat bizarre unless you think in terms of unrolling a loop *as a function*; there is no way to reference names from the current scope, and you will instead ignore the surrounding class and "reach out" into the next scope outwards (probably global scope).
Out of all the code in the stdlib, the *only* one that needed changing was in Lib/typing.py, where the above comprehension was found. (Not counting a couple of unit tests whose specific job is to verify this behaviour.)
If there are tests which intentionally verify this behaviour, that really hurts your position that the behaviour is an accident of implementation. It sounds like the behaviour is intended and required.
The potential for breakage is extremely low. Non-zero, but far lower than the cost of introducing a new keyword, for instance, which is done without deprecation cycles.
Which new keywords are you thinking of? The most recent new keywords I can think of were "True/False", "as" and "with". - True, False became keywords in 3.x during the "breaking code is allowed" 2 -> 3 transition; - "as" became a keyword in 2.6 following a deprecation period in 2.5: py> as = 1 <stdin>:1: Warning: 'as' will become a reserved keyword in Python 2.6 - and "with" needed a __future__ import. Have I missed any? -- Steve