2007-05-01 17:42:23 -04:00
|
|
|
|
PEP: 3132
|
|
|
|
|
Title: Extended Iterable Unpacking
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Georg Brandl <georg@python.org>
|
|
|
|
|
Status: Draft
|
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 30-Apr-2007
|
|
|
|
|
Python-Version: 3.0
|
|
|
|
|
Post-History:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
This PEP proposes a change to iterable unpacking syntax, allowing to
|
|
|
|
|
specify a "catch-all" name which will be assigned a list of all items
|
|
|
|
|
not assigned to a "regular" name.
|
|
|
|
|
|
|
|
|
|
An example says more than a thousand words::
|
|
|
|
|
|
|
|
|
|
>>> a, *b, c = range(5)
|
|
|
|
|
>>> a
|
|
|
|
|
0
|
|
|
|
|
>>> c
|
|
|
|
|
4
|
|
|
|
|
>>> b
|
|
|
|
|
[1, 2, 3]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
Many algorithms require splitting a sequence in a "first, rest" pair.
|
|
|
|
|
With the new syntax, ::
|
|
|
|
|
|
|
|
|
|
first, rest = seq[0], seq[1:]
|
|
|
|
|
|
|
|
|
|
is replaced by the cleaner and probably more efficient::
|
|
|
|
|
|
|
|
|
|
first, *rest = seq
|
|
|
|
|
|
|
|
|
|
For more complex unpacking patterns, the new syntax looks even
|
|
|
|
|
cleaner, and the clumsy index handling is not necessary anymore.
|
|
|
|
|
|
2007-05-02 17:24:11 -04:00
|
|
|
|
Also, if the right-hand value is not a list, but an iterable, it
|
|
|
|
|
has to be converted to a list before being able to do slicing; to
|
|
|
|
|
avoid creating this temporary list, one has to resort to ::
|
|
|
|
|
|
|
|
|
|
it = iter(seq)
|
|
|
|
|
first = it.next()
|
|
|
|
|
rest = list(it)
|
|
|
|
|
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
|
|
|
|
A tuple (or list) on the left side of a simple assignment (unpacking
|
|
|
|
|
is not defined for augmented assignment) may contain at most one
|
2007-05-02 17:24:11 -04:00
|
|
|
|
expression prepended with a single asterisk (which is henceforth
|
|
|
|
|
called a "starred" expression, while the other expressions in the
|
|
|
|
|
list are called "mandatory"). This designates a subexpression that
|
|
|
|
|
will be assigned a list of all items from the iterable being unpacked
|
|
|
|
|
that are not assigned to any of the mandatory expressions, or an
|
|
|
|
|
empty list if there are no such items.
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
2007-05-02 17:24:11 -04:00
|
|
|
|
For example, if ``seq`` is a slicable sequence, all the following
|
|
|
|
|
assignments are equivalent if ``seq`` has at least three elements::
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
2007-05-02 17:24:11 -04:00
|
|
|
|
a, b, c = seq[0], seq[1:-1], seq[-1]
|
|
|
|
|
a, *b, c = seq
|
|
|
|
|
[a, *b, c] = seq
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
|
|
|
|
It is an error (as it is currently) if the iterable doesn't contain
|
|
|
|
|
enough items to assign to all the mandatory expressions.
|
|
|
|
|
|
2007-05-02 17:24:11 -04:00
|
|
|
|
It is also an error to use the starred expression as a lone
|
|
|
|
|
assignment target, as in ::
|
|
|
|
|
|
|
|
|
|
*a = range(5)
|
|
|
|
|
|
|
|
|
|
This, however, is valid syntax::
|
|
|
|
|
|
|
|
|
|
*a, = range(5)
|
|
|
|
|
|
|
|
|
|
Note that this proposal also applies to tuples in implicit assignment
|
|
|
|
|
context, such as in a ``for`` statement::
|
|
|
|
|
|
2007-05-02 17:45:29 -04:00
|
|
|
|
for a, *b in [(1, 2, 3), (4, 5, 6, 7)]:
|
2007-05-02 17:24:11 -04:00
|
|
|
|
print(b)
|
|
|
|
|
|
|
|
|
|
would print out ::
|
|
|
|
|
|
|
|
|
|
[2, 3]
|
|
|
|
|
[5, 6, 7]
|
|
|
|
|
|
2007-05-02 17:45:29 -04:00
|
|
|
|
Starred expressions are only allowed as assignment targets, using them
|
|
|
|
|
anywhere else (except for star-args in function calls, of course) is an
|
|
|
|
|
error.
|
|
|
|
|
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
|
|
|
|
Implementation
|
|
|
|
|
==============
|
|
|
|
|
|
2007-05-02 17:24:11 -04:00
|
|
|
|
Grammar change
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
This feature requires a new grammar rule::
|
|
|
|
|
|
|
|
|
|
star_expr: ['*'] expr
|
|
|
|
|
|
|
|
|
|
In these two rules, ``expr`` is changed to ``star_expr``::
|
|
|
|
|
|
|
|
|
|
comparison: star_expr (comp_op star_expr)*
|
|
|
|
|
exprlist: star_expr (',' star_expr)* [',']
|
|
|
|
|
|
|
|
|
|
Changes to the Compiler
|
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
|
|
A new ASDL expression type ``Starred`` is added which represents a
|
|
|
|
|
starred expression. Note that the starred expression element
|
|
|
|
|
introduced here is universal and could later be used for other
|
|
|
|
|
purposes in non-assignment context, such as the ``yield *iterable``
|
|
|
|
|
proposal.
|
|
|
|
|
|
|
|
|
|
The compiler is changed to recognize all cases where a starred
|
|
|
|
|
expression is invalid and flag them with syntax errors.
|
|
|
|
|
|
|
|
|
|
A new bytecode instruction, ``UNPACK_EX``, is added, whose argument
|
|
|
|
|
has the number of mandatory targets before the starred target in the
|
|
|
|
|
lower 8 bits and the number of mandatory targets after the starred
|
|
|
|
|
target in the upper 8 bits. For unpacking sequences without starred
|
|
|
|
|
expressions, the old ``UNPACK_ITERABLE`` opcode is kept.
|
|
|
|
|
|
|
|
|
|
Changes to the Bytecode Interpreter
|
|
|
|
|
-----------------------------------
|
|
|
|
|
|
|
|
|
|
The function ``unpack_iterable()`` in ceval.c is changed to handle
|
|
|
|
|
the extended unpacking, via an ``argcntafter`` parameter. In the
|
|
|
|
|
``UNPACK_EX`` case, the function will do the following:
|
|
|
|
|
|
|
|
|
|
* collect all items for mandatory targets before the starred one
|
|
|
|
|
* collect all remaining items from the iterable in a list
|
|
|
|
|
* pop items for mandatory targets after the starred one from the list
|
|
|
|
|
* push the single items and the resized list on the stack
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
2007-05-02 17:24:11 -04:00
|
|
|
|
Shortcuts for unpacking iterables of known types, such as lists or
|
|
|
|
|
tuples, can be added.
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
|
|
|
|
|
2007-05-02 17:24:11 -04:00
|
|
|
|
The current implementation can be found at the SourceForge Patch
|
|
|
|
|
tracker [SFPATCH]_. It now includes a minimal test case.
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Open Issues
|
|
|
|
|
===========
|
|
|
|
|
|
|
|
|
|
- Should the catch-all expression be assigned a list or a tuple of items?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
==========
|
|
|
|
|
|
2007-05-02 17:24:11 -04:00
|
|
|
|
.. [SFPATCH] http://python.org/sf/1711529
|
2007-05-01 17:42:23 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|