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

Kirill Balunov kirillbalunov at gmail.com
Wed Apr 11 09:03:47 EDT 2018


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.


> Open questions
> ==============
>
> Can the outermost iterable still be evaluated early?
> ----------------------------------------------------
>
> As of Python 3.7, the outermost iterable in a genexp is evaluated early,
> and
> the result passed to the implicit function as an argument.  With PEP 572,
> this
> would no longer be the case. Can we still, somehow, evaluate it before
> moving
> on? One possible implementation would be::
>
>     gen = (x for x in rage(10))
>     # translates to
>     def <genexp>():
>         iterable = iter(rage(10))
>         yield None
>         for x in iterable:
>             yield x
>     gen = <genexp>()
>     next(gen)
>
> This would pump the iterable up to just before the loop starts, evaluating
> exactly as much as is evaluated outside the generator function in Py3.7.
> This would result in it being possible to call ``gen.send()`` immediately,
> unlike with most generators, and may incur unnecessary overhead in the
> common case where the iterable is pumped immediately (perhaps as part of a
> larger expression).
>
>
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.


> Frequently Raised Objections
> ============================
>
> Why not just turn existing assignment into an expression?
> ---------------------------------------------------------
>
> C and its derivatives define the ``=`` operator as an expression, rather
> than
> a statement as is Python's way.  This allows assignments in more contexts,
> including contexts where comparisons are more common.  The syntactic
> similarity
> between ``if (x == y)`` and ``if (x = y)`` belies their drastically
> different
> semantics.  Thus this proposal uses ``:=`` to clarify the distinction.
>
>
> 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:

Special-casing conditional statements
> -------------------------------------
>
> One of the most popular use-cases is ``if`` and ``while`` statements.
> Instead
> of a more general solution, this proposal enhances the syntax of these two
> statements to add a means of capturing the compared value::
>
>     if re.search(pat, text) as match:
>         print("Found:", match.group(0))
>
> This works beautifully if and ONLY if the desired condition is based on the
> truthiness of the captured value.  It is thus effective for specific
> use-cases (regex matches, socket reads that return `''` when done), and
> completely useless in more complicated cases (eg where the condition is
> ``f(x) < 0`` and you want to capture the value of ``f(x)``).  It also has
> no benefit to list comprehensions.
>
> Advantages: No syntactic ambiguities. Disadvantages: Answers only a
> fraction
> of possible use-cases, even in ``if``/``while`` statements.
>


(+ 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?

With kind regards,
-gdg
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180411/0a12af7f/attachment.html>


More information about the Python-ideas mailing list