Skip to content

Commit ccbe41e

Browse files
adqmJelleZijlstra
andauthored
gh-143055: Implementation of PEP 798 (#143056)
Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 96e4cd6 commit ccbe41e

File tree

14 files changed

+3270
-2109
lines changed

14 files changed

+3270
-2109
lines changed

Doc/reference/expressions.rst

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -266,17 +266,19 @@ called "displays", each of them in two flavors:
266266
Common syntax elements for comprehensions are:
267267

268268
.. productionlist:: python-grammar
269-
comprehension: `assignment_expression` `comp_for`
269+
comprehension: `flexible_expression` `comp_for`
270270
comp_for: ["async"] "for" `target_list` "in" `or_test` [`comp_iter`]
271271
comp_iter: `comp_for` | `comp_if`
272272
comp_if: "if" `or_test` [`comp_iter`]
273273

274274
The comprehension consists of a single expression followed by at least one
275-
:keyword:`!for` clause and zero or more :keyword:`!for` or :keyword:`!if` clauses.
276-
In this case, the elements of the new container are those that would be produced
277-
by considering each of the :keyword:`!for` or :keyword:`!if` clauses a block,
278-
nesting from left to right, and evaluating the expression to produce an element
279-
each time the innermost block is reached.
275+
:keyword:`!for` clause and zero or more :keyword:`!for` or :keyword:`!if`
276+
clauses. In this case, the elements of the new container are those that would
277+
be produced by considering each of the :keyword:`!for` or :keyword:`!if`
278+
clauses a block, nesting from left to right, and evaluating the expression to
279+
produce an element each time the innermost block is reached. If the expression
280+
is starred, the result will instead be unpacked to produce zero or more
281+
elements.
280282

281283
However, aside from the iterable expression in the leftmost :keyword:`!for` clause,
282284
the comprehension is executed in a separate implicitly nested scope. This ensures
@@ -321,6 +323,9 @@ See also :pep:`530`.
321323
asynchronous functions. Outer comprehensions implicitly become
322324
asynchronous.
323325

326+
.. versionchanged:: next
327+
Unpacking with the ``*`` operator is now allowed in the expression.
328+
324329

325330
.. _lists:
326331

@@ -396,8 +401,8 @@ enclosed in curly braces:
396401
.. productionlist:: python-grammar
397402
dict_display: "{" [`dict_item_list` | `dict_comprehension`] "}"
398403
dict_item_list: `dict_item` ("," `dict_item`)* [","]
404+
dict_comprehension: `dict_item` `comp_for`
399405
dict_item: `expression` ":" `expression` | "**" `or_expr`
400-
dict_comprehension: `expression` ":" `expression` `comp_for`
401406

402407
A dictionary display yields a new dictionary object.
403408

@@ -419,10 +424,21 @@ earlier dict items and earlier dictionary unpackings.
419424
.. versionadded:: 3.5
420425
Unpacking into dictionary displays, originally proposed by :pep:`448`.
421426

422-
A dict comprehension, in contrast to list and set comprehensions, needs two
423-
expressions separated with a colon followed by the usual "for" and "if" clauses.
424-
When the comprehension is run, the resulting key and value elements are inserted
425-
in the new dictionary in the order they are produced.
427+
A dict comprehension may take one of two forms:
428+
429+
- The first form uses two expressions separated with a colon followed by the
430+
usual "for" and "if" clauses. When the comprehension is run, the resulting
431+
key and value elements are inserted in the new dictionary in the order they
432+
are produced.
433+
434+
- The second form uses a single expression prefixed by the ``**`` dictionary
435+
unpacking operator followed by the usual "for" and "if" clauses. When the
436+
comprehension is evaluated, the expression is evaluated and then unpacked,
437+
inserting zero or more key/value pairs into the new dictionary.
438+
439+
Both forms of dictionary comprehension retain the property that if the same key
440+
is specified multiple times, the associated value in the resulting dictionary
441+
will be the last one specified.
426442

427443
.. index:: pair: immutable; object
428444
hashable
@@ -439,6 +455,8 @@ prevails.
439455
the key. Starting with 3.8, the key is evaluated before the value, as
440456
proposed by :pep:`572`.
441457

458+
.. versionchanged:: next
459+
Unpacking with the ``**`` operator is now allowed in dictionary comprehensions.
442460

443461
.. _genexpr:
444462

@@ -453,7 +471,7 @@ Generator expressions
453471
A generator expression is a compact generator notation in parentheses:
454472

455473
.. productionlist:: python-grammar
456-
generator_expression: "(" `expression` `comp_for` ")"
474+
generator_expression: "(" `flexible_expression` `comp_for` ")"
457475

458476
A generator expression yields a new generator object. Its syntax is the same as
459477
for comprehensions, except that it is enclosed in parentheses instead of

Doc/tutorial/classes.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,25 @@ Examples::
929929
>>> list(data[i] for i in range(len(data)-1, -1, -1))
930930
['f', 'l', 'o', 'g']
931931

932+
>>> x = [[1,2,3], [], [4, 5]]
933+
>>> g = (*i for i in x)
934+
>>> list(g)
935+
[1, 2, 3, 4, 5]
936+
937+
In most cases, generator expressions must be wrapped in parentheses. As a
938+
special case, however, when provided as the sole argument to a function (as in
939+
the examples involving ``sum``, ``set``, ``max``, and ``list`` above), the
940+
generator expression does not need to be wrapped in an additional set of
941+
parentheses. That is to say, the following two pieces of code are semantically
942+
equivalent::
943+
944+
>>> f(x for x in y)
945+
>>> f((x for x in y))
946+
947+
as are the following::
948+
949+
>>> f(*x for x in y)
950+
>>> f((*x for x in y))
932951

933952

934953
.. rubric:: Footnotes

Doc/tutorial/datastructures.rst

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,47 @@ The :func:`zip` function would do a great job for this use case::
333333

334334
See :ref:`tut-unpacking-arguments` for details on the asterisk in this line.
335335

336+
Unpacking in Lists and List Comprehensions
337+
------------------------------------------
338+
339+
The section on :ref:`tut-unpacking-arguments` describes the use of ``*`` to
340+
"unpack" the elements of an iterable object, providing each one seperately as
341+
an argument to a function. Unpacking can also be used in other contexts, for
342+
example, when creating lists. When specifying elements of a list, prefixing an
343+
expression by a ``*`` will unpack the result of that expression, adding each of
344+
its elements to the list we're creating::
345+
346+
>>> x = [1, 2, 3]
347+
>>> [0, *x, 4, 5, 6]
348+
[0, 1, 2, 3, 4, 5, 6]
349+
350+
This only works if the expression following the ``*`` evaluates to an iterable
351+
object; trying to unpack a non-iterable object will raise an exception::
352+
353+
>>> x = 1
354+
>>> [0, *x, 2, 3, 4]
355+
Traceback (most recent call last):
356+
File "<python-input-1>", line 1, in <module>
357+
[0, *x, 2, 3, 4]
358+
TypeError: Value after * must be an iterable, not int
359+
360+
Unpacking can also be used in list comprehensions, as a way to build a new list
361+
representing the concatenation of an arbitrary number of iterables::
362+
363+
>>> x = [[1, 2, 3], [4, 5, 6], [], [7], [8, 9]]
364+
>>> [*element for element in x]
365+
[1, 2, 3, 4, 5, 6, 7, 8, 9]
366+
367+
Note that the effect is that each element from ``x`` is unpacked. This works
368+
for arbitrary iterable objects, not just lists::
369+
370+
>>> x = [[1, 2, 3], 'cat', {'spam': 'eggs'}]
371+
>>> [*element for element in x]
372+
[1, 2, 3, 'c', 'a', 't', 'spam']
373+
374+
But if the objects in ``x`` are not iterable, this expression would again raise
375+
an exception.
376+
336377
.. _tut-del:
337378

338379
The :keyword:`!del` statement
@@ -394,7 +435,10 @@ A tuple consists of a number of values separated by commas, for instance::
394435
>>> v = ([1, 2, 3], [3, 2, 1])
395436
>>> v
396437
([1, 2, 3], [3, 2, 1])
397-
438+
>>> # they support unpacking just like lists:
439+
>>> x = [1, 2, 3]
440+
>>> 0, *x, 4
441+
(0, 1, 2, 3, 4)
398442

399443
As you see, on output tuples are always enclosed in parentheses, so that nested
400444
tuples are interpreted correctly; they may be input with or without surrounding
@@ -480,12 +524,16 @@ Here is a brief demonstration::
480524
{'r', 'd', 'b', 'm', 'z', 'l'}
481525

482526
Similarly to :ref:`list comprehensions <tut-listcomps>`, set comprehensions
483-
are also supported::
527+
are also supported, including comprehensions with unpacking::
484528

485529
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
486530
>>> a
487531
{'r', 'd'}
488532

533+
>>> fruits = [{'apple', 'avocado', 'apricot'}, {'banana', 'blueberry'}]
534+
>>> {*fruit for fruit in fruits}
535+
{'blueberry', 'banana', 'avocado', 'apple', 'apricot'}
536+
489537

490538
.. _tut-dictionaries:
491539

@@ -563,6 +611,18 @@ arbitrary key and value expressions::
563611
>>> {x: x**2 for x in (2, 4, 6)}
564612
{2: 4, 4: 16, 6: 36}
565613

614+
And dictionary unpacking (via ``**``) can be used to merge multiple
615+
dictionaries::
616+
617+
>>> odds = {i: i**2 for i in (1, 3, 5)}
618+
>>> evens = {i: i**2 for i in (2, 4, 6)}
619+
>>> {**odds, **evens}
620+
{1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36}
621+
622+
>>> all_values = [odds, evens, {0: 0}]
623+
>>> {**i for i in all_values}
624+
{1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36, 0: 0}
625+
566626
When the keys are simple strings, it is sometimes easier to specify pairs using
567627
keyword arguments::
568628

Doc/whatsnew/3.15.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ Summary -- Release highlights
6969
profiling tools <whatsnew315-profiling-package>`
7070
* :pep:`799`: :ref:`Tachyon: High frequency statistical sampling profiler
7171
profiling tools <whatsnew315-sampling-profiler>`
72+
* :pep:`798`: :ref:`Unpacking in Comprehensions
73+
<whatsnew315-unpacking-in-comprehensions>`
7274
* :pep:`686`: :ref:`Python now uses UTF-8 as the default encoding
7375
<whatsnew315-utf8-default>`
7476
* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
@@ -187,6 +189,45 @@ available output formats, profiling modes, and configuration options.
187189

188190
(Contributed by Pablo Galindo and László Kiss Kollár in :gh:`135953` and :gh:`138122`.)
189191

192+
.. _whatsnew315-unpacking-in-comprehensions:
193+
194+
:pep:`798`: Unpacking in Comprehensions
195+
---------------------------------------
196+
197+
List, set, and dictionary comprehensions, as well as generator expressions, now
198+
support unpacking with ``*`` and ``**``. This extends the unpacking syntax
199+
from :pep:`448` to comprehensions, providing a new syntax for combining an
200+
arbitrary number of iterables or dictionaries into a single flat structure.
201+
This new syntax is a direct alternative to nested comprehensions,
202+
:func:`itertools.chain`, and :meth:`itertools.chain.from_iterable`. For
203+
example::
204+
205+
>>> lists = [[1, 2], [3, 4], [5]]
206+
>>> [*L for L in lists] # equivalent to [x for L in lists for x in L]
207+
[1, 2, 3, 4, 5]
208+
209+
>>> sets = [{1, 2}, {2, 3}, {3, 4}]
210+
>>> {*s for s in sets} # equivalent to {x for s in sets for x in s}
211+
{1, 2, 3, 4}
212+
213+
>>> dicts = [{'a': 1}, {'b': 2}, {'a': 3}]
214+
>>> {**d for d in dicts} # equivalent to {k: v for d in dicts for k,v in d.items()}
215+
{'a': 3, 'b': 2}
216+
217+
Generator expressions can similarly use unpacking to yield values from multiple
218+
iterables::
219+
220+
>>> gen = (*L for L in lists) # equivalent to (x for L in lists for x in L)
221+
>>> list(gen)
222+
[1, 2, 3, 4, 5]
223+
224+
This change also extends to asynchronous generator expressions, such that, for
225+
example, ``(*a async for a in agen())`` is equivalent to ``(x async for a in
226+
agen() for x in a)``.
227+
228+
.. seealso:: :pep:`798` for further details.
229+
230+
(Contributed by Adam Hartz in :gh:`143055`.)
190231

191232
.. _whatsnew315-improved-error-messages:
192233

0 commit comments

Comments
 (0)