189 lines
5.1 KiB
Plaintext
189 lines
5.1 KiB
Plaintext
PEP: 379
|
||
Title: Adding an Assignment Expression
|
||
Version: $Revision$
|
||
Last-Modified: $Date$
|
||
Author: Jervis Whitley <jervisau@gmail.com>
|
||
Status: Withdrawn
|
||
Type: Standards Track
|
||
Content-Type: text/plain
|
||
Created: 14-Mar-2009
|
||
Python-Version: 2.7, 3.2
|
||
Post-History:
|
||
|
||
|
||
Abstract
|
||
|
||
This PEP adds a new assignment expression to the Python language
|
||
to make it possible to assign the result of an expression in
|
||
almost any place. The new expression will allow the assignment of
|
||
the result of an expression at first use (in a comparison for
|
||
example).
|
||
|
||
|
||
Motivation and Summary
|
||
|
||
Issue1714448 "if something as x:" [1] describes a feature to allow
|
||
assignment of the result of an expression in an if statement to a
|
||
name. It supposed that the 'as' syntax could be borrowed for this
|
||
purpose. Many times it is not the expression itself that is
|
||
interesting, rather one of the terms that make up the
|
||
expression. To be clear, something like this:
|
||
|
||
if (f_result() == [1, 2, 3]) as res:
|
||
|
||
seems awfully limited, when this:
|
||
|
||
if (f_result() as res) == [1, 2, 3]:
|
||
|
||
is probably the desired result.
|
||
|
||
|
||
Use Cases
|
||
|
||
See the Examples section near the end.
|
||
|
||
|
||
Specification
|
||
|
||
A new expression is proposed with the (nominal) syntax:
|
||
|
||
EXPR -> VAR
|
||
|
||
This single expression does the following:
|
||
|
||
- Evaluate the value of EXPR, an arbitrary expression;
|
||
- Assign the result to VAR, a single assignment target; and
|
||
- Leave the result of EXPR on the Top of Stack (TOS)
|
||
|
||
Here '->' or (RARROW) has been used to illustrate the concept that
|
||
the result of EXPR is assigned to VAR.
|
||
|
||
The translation of the proposed syntax is:
|
||
|
||
VAR = (EXPR)
|
||
(EXPR)
|
||
|
||
The assignment target can be either an attribute, a subscript or
|
||
name:
|
||
|
||
f() -> name[0] # where 'name' exists previously.
|
||
|
||
f() -> name.attr # again 'name' exists prior to this
|
||
expression.
|
||
|
||
f() -> name
|
||
|
||
This expression should be available anywhere that an expression is
|
||
currently accepted.
|
||
|
||
All exceptions that are currently raised during invalid
|
||
assignments will continue to be raised when using the assignment
|
||
expression. For example, a NameError will be raised when in
|
||
example 1 and 2 above if 'name' is not previously defined, or an
|
||
IndexError if index 0 was out of range.
|
||
|
||
|
||
Examples from the Standard Library
|
||
|
||
The following two examples were chosen after a brief search
|
||
through the standard library, specifically both are from ast.py
|
||
which happened to be open at the time of the search.
|
||
|
||
Original:
|
||
|
||
def walk(node):
|
||
from collections import deque
|
||
todo = deque([node])
|
||
while todo:
|
||
node = todo.popleft()
|
||
todo.extend(iter_child_nodes(node))
|
||
yield node
|
||
|
||
Using assignment expression:
|
||
|
||
def walk(node):
|
||
from collections import deque
|
||
todo = deque([node])
|
||
while todo:
|
||
todo.extend(iter_child_nodes(todo.popleft() -> node))
|
||
yield node
|
||
|
||
Original:
|
||
|
||
def get_docstring(node, clean=True):
|
||
if not isinstance(node, (FunctionDef, ClassDef, Module)):
|
||
raise TypeError("%r can't have docstrings"
|
||
% node.__class__.__name__)
|
||
if node.body and isinstance(node.body[0], Expr) and \
|
||
isinstance(node.body[0].value, Str):
|
||
if clean:
|
||
import inspect
|
||
return inspect.cleandoc(node.body[0].value.s)
|
||
return node.body[0].value.s
|
||
|
||
Using assignment expression:
|
||
|
||
def get_docstring(node, clean=True):
|
||
if not isinstance(node, (FunctionDef, ClassDef, Module)):
|
||
raise TypeError("%r can't have docstrings"
|
||
% node.__class__.__name__)
|
||
if node.body -> body and isinstance(body[0] -> elem, Expr) and \
|
||
isinstance(elem.value -> value, Str):
|
||
if clean:
|
||
import inspect
|
||
return inspect.cleandoc(value.s)
|
||
return value.s
|
||
|
||
|
||
Examples
|
||
|
||
The examples shown below highlight some of the desirable features
|
||
of the assignment expression, and some of the possible corner
|
||
cases.
|
||
|
||
1. Assignment in an if statement for use later.
|
||
|
||
def expensive():
|
||
import time; time.sleep(1)
|
||
return 'spam'
|
||
|
||
if expensive() -> res in ('spam', 'eggs'):
|
||
dosomething(res)
|
||
|
||
2. Assignment in a while loop clause.
|
||
|
||
while len(expensive() -> res) == 4:
|
||
dosomething(res)
|
||
|
||
3. Keep the iterator object from the for loop.
|
||
|
||
for ch in expensive() -> res:
|
||
sell_on_internet(res)
|
||
|
||
4. Corner case.
|
||
|
||
for ch -> please_dont in expensive():
|
||
pass
|
||
# who would want to do this? Not I.
|
||
|
||
|
||
References
|
||
|
||
[1] Issue1714448 "if something as x:", k0wax
|
||
http://bugs.python.org/issue1714448
|
||
|
||
|
||
Copyright
|
||
|
||
This document has been placed in the public domain.
|
||
|
||
|
||
|
||
Local Variables:
|
||
mode: indented-text
|
||
indent-tabs-mode: nil
|
||
sentence-end-double-space: t
|
||
fill-column: 70
|
||
coding: utf-8
|
||
End:
|