Update to PEP 448 -- approved 2 out of 3, rejected *comprehension.

This commit is contained in:
Guido van Rossum 2015-02-26 12:17:47 -08:00
parent f2db585e09
commit 401c977971
1 changed files with 65 additions and 50 deletions

View File

@ -4,11 +4,11 @@ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: Joshua Landau <joshua@landau.ws> Author: Joshua Landau <joshua@landau.ws>
Discussions-To: python-ideas@python.org Discussions-To: python-ideas@python.org
Status: Draft Status: Accepted
Type: Standards Track Type: Standards Track
Content-Type: text/x-rst Content-Type: text/x-rst
Created: 29-Jun-2013 Created: 29-Jun-2013
Python-Version: 3.4 Python-Version: 3.5
Post-History: Post-History:
@ -50,15 +50,9 @@ In dictionaries, later values will always override earlier ones::
>>> {**{'x': 2}, 'x': 1} >>> {**{'x': 2}, 'x': 1}
{'x': 1} {'x': 1}
Unpacking is proposed to be allowed inside list, set, This PEP does not include unpacking operators inside list, set and
and dictionary comprehensions:: dictionary comprehensions although this has not been ruled out for
future proposals.
>>> ranges = [range(i) for i in range(5)]
>>> [*item for item in ranges]
[0, 0, 1, 0, 1, 2, 0, 1, 2, 3]
>>> {*item for item in ranges}
{0, 1, 2, 3}
Rationale Rationale
@ -121,14 +115,6 @@ list(my_range)`` which is now equivalent to just ``[*my_list,
*my_tuple, *my_range]``. *my_tuple, *my_range]``.
The addition of unpacking to comprehensions is a logical extension.
It's usage will primarily be a neat replacement for ``[i for j in
list_of_lists for i in j]``, as the more readable
``[*l for l in list_of_lists]``. The iterable version,
``(*l for l in list_of_lists)``, replaces ``itertools.chain.from_iterable``.
Other uses are possible, but expected to occur rarely.
Specification Specification
============= =============
@ -145,7 +131,8 @@ Currently, if an argument is given multiple times — such as a
positional argument given both positionally and by keyword — a positional argument given both positionally and by keyword — a
``TypeError`` is raised. This remains true for duplicate arguments ``TypeError`` is raised. This remains true for duplicate arguments
provided through multiple ``**`` unpackings, provided through multiple ``**`` unpackings,
e.g. ``f(**{'x': 2}, **{'x': 3})``. e.g. ``f(**{'x': 2}, **{'x': 3})``, except that the error will be
detected at runtime.
A function looks like this:: A function looks like this::
@ -159,34 +146,11 @@ Tuples, lists, sets and dictionaries will allow unpacking. This will
act as if the elements from unpacked items were inserted in order at act as if the elements from unpacked items were inserted in order at
the site of unpacking, much as happens in unpacking in a function-call. the site of unpacking, much as happens in unpacking in a function-call.
Dictionaries require ``**`` unpacking; all the others require ``*`` unpacking. Dictionaries require ``**`` unpacking; all the others require ``*`` unpacking.
A dictionary's key remain in a right-to-left priority order, so
The keys in a dictionary remain in a right-to-left priority order, so
``{**{'a': 1}, 'a': 2, **{'a': 3}}`` evaluates to ``{'a': 3}``. There ``{**{'a': 1}, 'a': 2, **{'a': 3}}`` evaluates to ``{'a': 3}``. There
is no restriction on the number or position of unpackings. is no restriction on the number or position of unpackings.
Comprehensions, by simple extension, will support unpacking. As before,
dictionaries require ``**`` unpacking, all the others require ``*``
unpacking and key priorities are unchanged.
For example::
{*[1, 2, 3], 4, 5, *{6, 7, 8}}
(*e for e in [[1], [3, 4, 5], [2]])
{**dictionary for dictionary in (globals(), locals())}
{**locals(), "override": None}
Unbracketed comprehensions in function calls, such as ``f(x for x in it)``,
are already valid. These could be extended to::
f(*x for x in it) == f((*x for x in it))
f(**x for x in it) == f({**x for x in it})
However, this is likely to be confusing and is not included in this
PEP. These will throw ``SyntaxError`` and comprehensions with explicit
brackets should be used instead.
Disadvantages Disadvantages
============= =============
@ -200,24 +164,75 @@ Whilst ``*elements, = iterable`` causes ``elements`` to be a list,
``elements = *iterable,`` causes ``elements`` to be a tuple. The ``elements = *iterable,`` causes ``elements`` to be a tuple. The
reason for this may confuse people unfamiliar with the construct. reason for this may confuse people unfamiliar with the construct.
Concerns have been raised about the unexpected difference between
duplicate keys in dictionaries being allowed but duplicate keys
in function call syntax raising an error. Although this is already
the case with current syntax, this proposal might exacerbate the
issue. It remains to be seen how much of an issue this is in practice.
Variations
==========
The PEP originally considered whether the ordering of argument types
in a function call (positional, keyword, ``*`` or ``**``) could become
less strict. This met little support so the idea was shelved.
Earlier iterations of this PEP allowed unpacking operators inside
list, set, and dictionary comprehensions as a flattening operator
over iterables of containers::
>>> ranges = [range(i) for i in range(5)]
>>> [*item for item in ranges]
[0, 0, 1, 0, 1, 2, 0, 1, 2, 3]
>>> {*item for item in ranges}
{0, 1, 2, 3}
This was met with a mix of strong concerns about readability and mild
support. In order not to disadvantage the less controversial aspects
of the PEP, this was not accepted with the rest of the proposal.
Unbracketed comprehensions in function calls, such as ``f(x for x in it)``,
are already valid. These could be extended to::
f(*x for x in it) == f((*x for x in it))
f(**x for x in it) == f({**x for x in it})
However, it wasn't clear if this was the best behaviour or if it should
unpack into the arguments of the call to `f`. Since this is likely to be
confusing and is of only very marginal utility, it is not included in this
PEP. Instead, these will throw a ``SyntaxError`` and comprehensions with
explicit brackets should be used instead.
Approval
========
This PEP was accepted by Guido on February 25, 2015 [1]_.
Implementation Implementation
============== ==============
An implementation for Python 3.5 is found at Issue 2292 on bug tracker [1]_. An implementation for Python 3.5 is found at Issue 2292 on bug tracker [2]_.
This currently includes support for unpacking inside comprehensions, which
should be removed.
References References
========== ==========
.. [1] Issue 2292, "Missing `*`-unpacking generalizations", Thomas Wouters .. [1] PEP accepted, "PEP 448 review", Guido van Rossum
(https://mail.python.org/pipermail/python-dev/2015-February/138564.html)
.. [2] Issue 2292, "Missing `*`-unpacking generalizations", Thomas Wouters
(http://bugs.python.org/issue2292) (http://bugs.python.org/issue2292)
.. [2] Discussion on Python-ideas list, .. [3] Discussion on Python-ideas list,
"list / array comprehensions extension", Alexander Heger "list / array comprehensions extension", Alexander Heger
(http://mail.python.org/pipermail/python-ideas/2011-December/013097.html) (http://mail.python.org/pipermail/python-ideas/2011-December/013097.html)
Copyright Copyright
========= =========