[Python-Dev] PEP 572: Assignment Expressions
Steven D'Aprano
steve at pearwood.info
Tue Apr 17 21:20:48 EDT 2018
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
More information about the Python-Dev
mailing list