PEP 572: Final updates prior to posting to python-dev

This commit is contained in:
Chris Angelico 2018-04-17 17:40:51 +10:00
parent 1129ab3aa3
commit 73715149df
1 changed files with 58 additions and 33 deletions

View File

@ -6,7 +6,7 @@ Type: Standards Track
Content-Type: text/x-rst
Created: 28-Feb-2018
Python-Version: 3.8
Post-History: 28-Feb-2018, 02-Mar-2018, 23-Mar-2018, 04-Apr-2018
Post-History: 28-Feb-2018, 02-Mar-2018, 23-Mar-2018, 04-Apr-2018, 17-Apr-2018
Abstract
@ -37,7 +37,7 @@ Syntax and semantics
In any context where arbitrary Python expressions can be used, a **named
expression** can appear. This is of the form ``target := expr`` where
``expr`` is any valid Python expression, and ``target`` is any valid
assignment target. (NOTE: See 'Open questions' below for precedence.)
assignment target.
The value of such a named expression is the same as the incorporated
expression, with the additional side-effect that the target is assigned
@ -61,11 +61,12 @@ Differences from regular assignment statements
Most importantly, since ``:=`` is an expression, it can be used in contexts
where statements are illegal, including lambda functions and comprehensions.
An assignment statement can assign to multiple targets::
An assignment statement can assign to multiple targets, left-to-right::
x = y = z = 0
To do the same with assignment expressions, they must be parenthesized::
The equivalent assignment expression is parsed as separate binary operators,
and is therefore processed right-to-left, as if it were spelled thus::
assert 0 == (x := (y := (z := 0)))
@ -77,7 +78,8 @@ Augmented assignment is not supported in expression form::
^
SyntaxError: invalid syntax
Otherwise, the semantics of assignment are unchanged by this proposal.
Otherwise, the semantics of assignment are identical in statement and
expression forms.
Alterations to comprehensions
@ -184,9 +186,6 @@ These list comprehensions are all approximately equivalent::
# There are a number of less obvious ways to spell this in current
# versions of Python.
# Calling the function twice
stuff = [[f(x), x/f(x)] for x in range(5)]
# External helper function
def pair(x, value): return [value, x/value]
stuff = [pair(x, f(x)) for x in range(5)]
@ -410,6 +409,31 @@ in a statement; alternatively, a new keyword is needed, with all the costs
therein.
Lowering operator precedence
----------------------------
There are two logical precedences for the ``:=`` operator. Either it should
bind as loosely as possible, as does statement-assignment; or it should bind
more tightly than comparison operators. Placing its precedence between the
comparison and arithmetic operators (to be precise: just lower than bitwise
OR) allows most uses inside ``while`` and ``if`` conditions to be spelled
without parentheses, as it is most likely that you wish to capture the value
of something, then perform a comparison on it::
pos = -1
while pos := buffer.find(search_term, pos + 1) >= 0:
...
Once find() returns -1, the loop terminates. If ``:=`` binds as loosely as
``=`` does, this would capture the result of the comparison (generally either
``True`` or ``False``), which is less useful.
While this behaviour would be convenient in many situations, it is also harder
to explain than "the := operator behaves just like the assignment statement",
and as such, the precedence for ``:=`` has been made as close as possible to
that of ``=``.
Migration path
==============
@ -440,6 +464,26 @@ to yield values (and is thus a generator function). The entire comprehension
is consistently in a single scope.
Name reuse inside comprehensions
--------------------------------
If the same name is used in the outermost iterable and also as an iteration
variable, this will now raise UnboundLocalError when previously it referred
to the name in the surrounding scope. Example::
# Lib/typing.py
tvars = []
for t in types:
if isinstance(t, TypeVar) and t not in tvars:
tvars.append(t)
if isinstance(t, _GenericAlias) and not t._special:
tvars.extend([ty for ty in t.__parameters__ if ty not in tvars])
If the list comprehension uses the name ``t`` rather than ``ty``, this will
work in Python 3.7 but not with this proposal. As with other unwanted name
shadowing, the solution is to use distinct names.
Name lookups in class scope
---------------------------
@ -477,7 +521,9 @@ function form::
gen = <genexp>() # No exception yet
tng = next(gen) # NameError
To detect these errors more quickly, ... TODO.
Detecting these errors more quickly is nontrivial. It is, however, the exact
same problem as generator functions currently suffer from, and this proposal
brings the genexp in line with the most natural longhand form.
Open questions
@ -548,26 +594,6 @@ Translated into longhand, this would become::
ie utilizing the same early-binding technique that is used at class scope.
Operator precedence
-------------------
There are two logical precedences for the ``:=`` operator. Either it should
bind as loosely as possible, as does statement-assignment; or it should bind
more tightly than comparison operators. Placing its precedence between the
comparison and arithmetic operators (to be precise: just lower than bitwise
OR) allows most uses inside ``while`` and ``if`` conditions to be spelled
without parentheses, as it is most likely that you wish to capture the value
of something, then perform a comparison on it::
pos = -1
while pos := buffer.find(search_term, pos + 1) >= 0:
...
Once find() returns -1, the loop terminates. If ``:=`` binds as loosely as
``=`` does, this would capture the result of the comparison (generally either
``True`` or ``False``), which is less useful.
Frequently Raised Objections
============================
@ -592,10 +618,9 @@ With assignment expressions, why bother with assignment statements?
-------------------------------------------------------------------
The two forms have different flexibilities. The ``:=`` operator can be used
inside a larger expression; the ``=`` operator can be chained more
conveniently, and closely parallels the inline operations ``+=`` and friends.
The assignment statement is a clear declaration of intent: this value is to
be assigned to this target, and that's it.
inside a larger expression; the ``=`` statement can be augmented to ``+=`` and
its friends. The assignment statement is a clear declaration of intent: this
value is to be assigned to this target, and that's it.
Why not use a sublocal scope and prevent namespace pollution?