[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