197 lines
4.9 KiB
Plaintext
197 lines
4.9 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/x-rst
|
|
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:
|