PEP 572: Final updates prior to posting to python-dev
This commit is contained in:
parent
1129ab3aa3
commit
73715149df
91
pep-0572.rst
91
pep-0572.rst
|
@ -6,7 +6,7 @@ Type: Standards Track
|
||||||
Content-Type: text/x-rst
|
Content-Type: text/x-rst
|
||||||
Created: 28-Feb-2018
|
Created: 28-Feb-2018
|
||||||
Python-Version: 3.8
|
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
|
Abstract
|
||||||
|
@ -37,7 +37,7 @@ Syntax and semantics
|
||||||
In any context where arbitrary Python expressions can be used, a **named
|
In any context where arbitrary Python expressions can be used, a **named
|
||||||
expression** can appear. This is of the form ``target := expr`` where
|
expression** can appear. This is of the form ``target := expr`` where
|
||||||
``expr`` is any valid Python expression, and ``target`` is any valid
|
``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
|
The value of such a named expression is the same as the incorporated
|
||||||
expression, with the additional side-effect that the target is assigned
|
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
|
Most importantly, since ``:=`` is an expression, it can be used in contexts
|
||||||
where statements are illegal, including lambda functions and comprehensions.
|
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
|
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)))
|
assert 0 == (x := (y := (z := 0)))
|
||||||
|
|
||||||
|
@ -77,7 +78,8 @@ Augmented assignment is not supported in expression form::
|
||||||
^
|
^
|
||||||
SyntaxError: invalid syntax
|
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
|
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
|
# There are a number of less obvious ways to spell this in current
|
||||||
# versions of Python.
|
# versions of Python.
|
||||||
|
|
||||||
# Calling the function twice
|
|
||||||
stuff = [[f(x), x/f(x)] for x in range(5)]
|
|
||||||
|
|
||||||
# External helper function
|
# External helper function
|
||||||
def pair(x, value): return [value, x/value]
|
def pair(x, value): return [value, x/value]
|
||||||
stuff = [pair(x, f(x)) for x in range(5)]
|
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.
|
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
|
Migration path
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
@ -440,6 +464,26 @@ to yield values (and is thus a generator function). The entire comprehension
|
||||||
is consistently in a single scope.
|
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
|
Name lookups in class scope
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
@ -477,7 +521,9 @@ function form::
|
||||||
gen = <genexp>() # No exception yet
|
gen = <genexp>() # No exception yet
|
||||||
tng = next(gen) # NameError
|
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
|
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.
|
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
|
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
|
The two forms have different flexibilities. The ``:=`` operator can be used
|
||||||
inside a larger expression; the ``=`` operator can be chained more
|
inside a larger expression; the ``=`` statement can be augmented to ``+=`` and
|
||||||
conveniently, and closely parallels the inline operations ``+=`` and friends.
|
its friends. The assignment statement is a clear declaration of intent: this
|
||||||
The assignment statement is a clear declaration of intent: this value is to
|
value is to be assigned to this target, and that's it.
|
||||||
be assigned to this target, and that's it.
|
|
||||||
|
|
||||||
|
|
||||||
Why not use a sublocal scope and prevent namespace pollution?
|
Why not use a sublocal scope and prevent namespace pollution?
|
||||||
|
|
Loading…
Reference in New Issue