PEP 572: Address several small issues (#709)

- Dropped "TODO: Include Guido's evidence, and do a more systematic search."
  I think the current text is good enough.

- Relented on assignment expressions in default values other than top
  level.  (Mainly because the syntactic/semantic check would be
  awkward, and it was already called out as a possible style
  recommendation instead.)

- Clarified that a lambda counts as "the current scope" (containing an
  assignment expression).

- Added prohibition on [... for i in i := ...].

- Specified that scope-related prohibitions should raise
  TargetScopeError, a subclass of SyntaxError.
This commit is contained in:
Guido van Rossum 2018-07-08 21:35:44 -07:00 committed by GitHub
parent 3e128e568b
commit a9b8753635
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 19 additions and 25 deletions

View File

@ -111,8 +111,6 @@ efficient rewrite would have been::
if match2:
return match2.group(2)
(TODO: Include Guido's evidence, and do a more systematic search.)
Syntax and semantics
====================
@ -144,50 +142,45 @@ There are a few places where assignment expressions are not allowed,
in order to avoid ambiguities or user confusion:
- Unparenthesized assignment expressions are prohibited at the top
level of an expression statement; for example, this is not allowed::
level of an expression statement. Example::
y := f(x) # INVALID
(y := f(x)) # Valid, though not recommended
This rule is included to simplify the choice for the user between an
assignment statements and an assignment expression -- there is no
syntactic position where both are valid.
- Unparenthesized assignment expressions are prohibited at the top
level in the right hand side of an assignment statement; for
example, the following is not allowed::
level of the right hand side of an assignment statement. Example::
y0 = y1 := f(x) # INVALID
y0 = (y1 := f(x)) # Valid, though discouraged
Again, this rule is included to avoid two visually similar ways of
saying the same thing.
- Unparenthesized assignment expressions are prohibited for the value
of a keyword argument in a call; for example, this is disallowed::
of a keyword argument in a call. Example::
foo(x = y := f(x)) # INVALID
foo(x=(y := f(x))) # Valid, though probably confusing
This rule is included to disallow excessively confusing code, and
because parsing keyword arguments is complex enough already.
- Assignment expressions (even parenthesized or occurring inside other
constructs) are prohibited in function default values. For example,
the following examples are all invalid, even though the expressions
for the default values are valid in other contexts::
- Unparenthesized assignment expressions are prohibited at the top
level of a function default value. Example::
def foo(answer = p := 42): # INVALID
...
def bar(answer = (p := 42)): # INVALID
def foo(answer=(p := 42)): # Valid, though not great style
...
def baz(callback = (lambda arg: p := arg)): # INVALID
...
This rule is included to avoid side effects in a position whose
This rule is included to discourage side effects in a position whose
exact semantics are already confusing to many users (cf. the common
style recommendation against mutable default values). (TODO: Maybe
this should just be a style recommendation except for the
prohibition at the top level?)
style recommendation against mutable default values), and also to
echo the similar prohibition in calls (the previous bullet).
Scope of the target
-------------------
@ -196,7 +189,8 @@ An assignment expression does not introduce a new scope. In most
cases the scope in which the target will be bound is self-explanatory:
it is the current scope. If this scope contains a ``nonlocal`` or
``global`` declaration for the target, the assignment expression
honors that.
honors that. A lambda (being an explicit, if anonymous, function
definition) counts as a scope for this purpose.
There is one special case: an assignment expression occurring in a
list, set or dict comprehension or in a generator expression (below
@ -231,12 +225,11 @@ comprehension, for example::
An exception to this special case applies when the target name is the
same as a loop control variable for a comprehension containing it.
This is invalid. (This exception exists to rule out edge cases of the
This is invalid. This exception exists to rule out edge cases of the
above scope rules as illustrated by ``[i := i+1 for i in range(5)]``
or ``[[(j := j) for i in range(5)] for j in range(5)]``. Note that
this exception also applies to ``[i := 0 for i, j in stuff]``.)
TODO: prohibit [... for i in i := ...]
this exception also applies to ``[i := 0 for i, j in stuff]``, as well
as to cases like ``[i+1 for i in i := stuff]``.
A further exception applies when an assignment expression occurrs in a
comprehension whose containing scope is a class scope. If the rules
@ -254,7 +247,8 @@ variable defined in the class scope from a comprehension.)
See Appendix B for some examples of how the rules for targets in
comprehensions translate to equivalent code.
TODO: use a a subclass of SyntaxError for various prohibitions?
The two invalid cases listed above raise ``TargetScopeError``, a
subclass of ``SyntaxError`` (with the same signature).
Relative precedence of ``:=``
-----------------------------