[Python-ideas] PEP 572: Assignment Expressions (post #4)

Chris Angelico rosuav at gmail.com
Wed Apr 11 09:50:44 EDT 2018


On Wed, Apr 11, 2018 at 11:03 PM, Kirill Balunov
<kirillbalunov at gmail.com> wrote:
> Great work Chris! Thank you!
>
> I do not know whether this is good or bad, but this PEP considers so many
> different topics, although closely interrelated with each other.
>
> 2018-04-11 8:32 GMT+03:00 Chris Angelico <rosuav at gmail.com>:
>>
>>
>> Alterations to comprehensions
>> -----------------------------
>>
>> The current behaviour of list/set/dict comprehensions and generator
>> expressions has some edge cases that would behave strangely if an
>> assignment
>> expression were to be used. Therefore the proposed semantics are changed,
>> removing the current edge cases, and instead altering their behaviour
>> *only*
>> in a class scope.
>>
>> As of Python 3.7, the outermost iterable of any comprehension is evaluated
>> in the surrounding context, and then passed as an argument to the implicit
>> function that evaluates the comprehension.
>>
>> Under this proposal, the entire body of the comprehension is evaluated in
>> its implicit function. Names not assigned to within the comprehension are
>> located in the surrounding scopes, as with normal lookups. As one special
>> case, a comprehension at class scope will **eagerly bind** any name which
>> is already defined in the class scope.
>>
>
> I think this change is important one no matter what will be the future of
> the current PEP. And since it breaks backward compatibility it deserves a
> separate PEP.

Well, it was Guido himself who started the sub-thread about classes
and comprehensions :)

To be honest, the changes to comprehensions are mostly going to be
under-the-hood tweaks. The only way you'll ever actually witness the
changes are if you:

1) Use assignment expressions inside comprehensions (ie using both
halves of this PEP); or

2) Put comprehensions at class scope (not inside methods, but actually
at class scope), referring to other names from class scope, in places
other than in the outermost iterable

3) Use 'yield' expressions in the outermost iterable of a list
comprehension inside a generator function

4) Create a generator expression that refers to an external name, then
change what that name is bound to before pumping the generator;
depending on the one open question, this may occur ONLY if this
external name is located at class scope.

5) Use generator expressions without iterating over them, in
situations where iterating might fail (again, depends on the one open
question).

Aside from the first possibility, these are extremely narrow edge and
corner cases, and the new behaviour is generally the more intuitive
anyway. Class scope stops being so incredibly magical that it's
completely ignored, and now becomes mildly magical such that name
lookups are resolved eagerly instead of lazily; and the outermost
iterable stops being magical in that it defies the weirdness of class
scope and the precise definitions of generator functions. Special
cases are being removed, not added.

>> Open questions
>> ==============
>>
>> Can the outermost iterable still be evaluated early?
>> ----------------------------------------------------
>>
>
> Previously, there was an alternative _operator form_  `->`  proposed by
> Steven D'Aprano. This option is no longer considered? I see several
> advantages with this variant:
> 1. It does not use `:` symbol which is very visually overloaded in Python.
> 2. It is clearly distinguishable from the usual assignment statement and
> it's `+=` friends
> There are others but they are minor.

I'm not sure why you posted this in response to the open question, but
whatever. The arrow operator is already a token in Python (due to its
use in 'def' statements) and should not conflict with anything;
however, apart from "it looks different", it doesn't have much to
speak for it. The arrow faces the other way in languages like Haskell,
but we can't use "<-" in Python due to conflicts with "<" and "-" as
independent operators.

>> This could be used to create ugly code!
>> ---------------------------------------
>>
>> So can anything else.  This is a tool, and it is up to the programmer to
>> use it
>> where it makes sense, and not use it where superior constructs can be
>> used.
>>
>
> But the ugly code matters, especially when it comes to Python. For me, the
> ideal option would be the combination of two rejected parts:
>
> (+ in `while`) combined with this part:
>
>
>> 3. ``with EXPR as NAME``::
>>
>>        stuff = [(y, x/y) with f(x) as y for x in range(5)]
>>
>>    As per option 2, but using ``as`` rather than an equals sign. Aligns
>>    syntactically with other uses of ``as`` for name binding, but a simple
>>    transformation to for-loop longhand would create drastically different
>>    semantics; the meaning of ``with`` inside a comprehension would be
>>    completely different from the meaning as a stand-alone statement, while
>>    retaining identical syntax.
>
>
> I see no benefit to have the assignment expression in other places. And all
> your provided examples use `while` or `if` or some form of comprehension. I
> also see no problem with `if (re.search(pat, text) as match) is not
> None:..`.  What is the point of overloading language with expression that
> will be used only in `while` and `if` and will be rejected by style checkers
> in other places?

Can you give an example of how your syntax is superior to the more
general option of simply allowing "as" bindings in any location?

ChrisA


More information about the Python-ideas mailing list