Skip to content

Commit 2bd9217

Browse files
authored
PEP 798: Mark as Accepted (#4758)
1 parent 8e4a7b6 commit 2bd9217

File tree

1 file changed

+41
-50
lines changed

1 file changed

+41
-50
lines changed

peps/pep-0798.rst

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ Title: Unpacking in Comprehensions
33
Author: Adam Hartz <[email protected]>, Erik Demaine <[email protected]>
44
Sponsor: Jelle Zijlstra <jelle.zijlstra at gmail.com>
55
Discussions-To: https://discuss.python.org/t/99435
6-
Status: Draft
6+
Status: Accepted
77
Type: Standards Track
88
Created: 19-Jul-2025
99
Python-Version: 3.15
1010
Post-History: `16-Oct-2021 <https://mail.python.org/archives/list/[email protected]/thread/7G732VMDWCRMWM4PKRG6ZMUKH7SUC7SH/>`__, `22-Jun-2025 <https://discuss.python.org/t/pre-pep-unpacking-in-comprehensions/96362>`__, `19-Jul-2025 <https://discuss.python.org/t/pep-798-unpacking-in-comprehensions/99435>`__
11+
Resolution: `03-Nov-2025 <https://discuss.python.org/t/pep-798-unpacking-in-comprehensions/99435/60>`__
1112

1213

1314
Abstract
@@ -109,7 +110,7 @@ number of iterables.
109110

110111
This proposal represents a natural extension of the language, paralleling
111112
existing syntactic structures: where ``[x, y, z]`` creates a list from a fixed
112-
number of vaues, ``[item for item in items]`` creates a list from an arbitrary
113+
number of values, ``[item for item in items]`` creates a list from an arbitrary
113114
number of values; this proposal extends that notion to the construction of
114115
lists that involve unpacking, making ``[*item for item in items]`` analogous to
115116
``[*x, *y, *z]``.
@@ -222,19 +223,17 @@ Semantics: Generator Expressions
222223
Generator expressions using the unpacking syntax should form new generators
223224
producing values from the concatenation of the iterables given by the
224225
expressions. Specifically, the behavior is defined to be equivalent to the
225-
following::
226+
following (though without defining or referencing the looping variable ``i``)::
226227

227228
# equivalent to g = (*expr for x in it)
228229
def generator():
229230
for x in it:
230-
yield from expr
231+
for i in expr:
232+
yield i
231233

232234
g = generator()
233235

234-
Since ``yield from`` is not allowed inside of async generators (see the section
235-
of :pep:`525` on Asynchronous ``yield from``), the equivalent for ``(*expr
236-
async for x in ait())`` is more like the following (though of course this new
237-
form should not define or reference the looping variable ``i``)::
236+
.. code:: python
238237
239238
# equivalent to g = (*expr async for x in ait())
240239
async def generator():
@@ -244,11 +243,8 @@ form should not define or reference the looping variable ``i``)::
244243
245244
g = generator()
246245
247-
The specifics of these semantics should be revisited in the future,
248-
particularly if async generators receive support for ``yield from`` (in which
249-
case the async variant may wish to be changed to make use of ``yield from``
250-
instead of an explicit loop). See :ref:`pep798-alternativegenexpsemantics` for
251-
more discussion.
246+
247+
See :ref:`pep798-alternativegenexpsemantics` for more discussion of these semantics.
252248

253249
Interaction with Assignment Expressions
254250
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -267,7 +263,8 @@ form, ``y`` will be bound in the containing scope instead of locally::
267263

268264
def generator():
269265
for i in (0, 2, 4):
270-
yield from (y := [i, i+1])
266+
for j in (y := [i, i+1]):
267+
yield j
271268

272269
In this example, the subexpression ``(y := [i, i+1])`` is evaluated exactly
273270
three times before the generator is exhausted: just after assigning ``i`` in
@@ -402,13 +399,14 @@ particular phrasing of any of the old error messages being replaced, which we
402399
expect to be rare.
403400

404401
One related concern is that a hypothetical future decision to change the
405-
semantics of async generator expressions to make use of ``yield from`` during
402+
semantics of generator expressions to make use of ``yield from`` during
406403
unpacking (delegating to generators that are being unpacked) would not be
407404
backwards-compatible because it would affect the behavior of the resulting
408-
generators when used with ``.asend()``, ``.athrow()``, and ``.aclose()``. That
409-
said, despite being backwards-incompatible, such a change would be unlikely to
410-
have a large impact because it would only affect the behavior of structures
411-
that, under this proposal, are not particularly useful. See
405+
generators when used with ``.send()``/``.asend()``, ``.throw()``/``.athrow()``,
406+
and ``.close()``/``.aclose()``. That said, despite being
407+
backwards-incompatible, such a change would be unlikely to have a large impact
408+
because it would only affect the behavior of structures that, under this
409+
proposal, are not particularly useful. See
412410
:ref:`pep798-alternativegenexpsemantics` for more discussion.
413411

414412
.. _pep798-examples:
@@ -759,59 +757,52 @@ a `poll in the Discourse thread
759757
<https://discuss.python.org/t/pep-798-unpacking-in-comprehensions/99435/33>`__.
760758
Beyond the proposal outlined above, the following were also considered:
761759

762-
1. Using explicit loops for both synchronous and asynchronous generator
763-
expressions.
760+
1. Using ``yield from`` for unpacking in synchronous generator expressions but
761+
using an explicit loop in asynchronous generator expressions (as proposed in
762+
the original draft of this PEP).
764763

765-
This strategy would have resulted in a symmetry between synchronous and
766-
asynchronous generator expressions but would have prevented a
767-
potentially-useful tool by disallowing delegation in the case of synchronous
768-
generator expressions. One specific concern with this approach is the
769-
introduction of an asymmetry between synchronous and asynchronous
770-
generators, but this concern is mitigated by the fact that these asymmetries
771-
already exist between synchronous and asynchronous generators more
772-
generally.
764+
This strategy would have allowed unpacking in generator expressions to
765+
closely mimic a popular way of writing generators that perform this
766+
operation (using ``yield from``), but it would also have created an
767+
asymmetry between synchronous and asynchronous versions, and also between
768+
this new syntax and ``itertools.chain`` and the double-loop version.
773769

774770
2. Using ``yield from`` for unpacking in synchronous generator expressions and
775771
mimicking the behavior of ``yield from`` for unpacking in async generator
776772
expressions.
777773

778774
This strategy would also make unpacking in synchronous and asynchronous
779775
generators behave symmetrically, but it would also be more complex, enough
780-
so that the cost may not be worth the benefit. As such, this PEP proposes
781-
that asynchronous generator expressions using the unpacking operator should
782-
not adopt semantics similar to ``yield from`` until ``yield from`` is
783-
supported in asynchronous generators more generally.
776+
so that the cost may not be worth the benefit, particularly in the absence
777+
of a compelling use case for delegation.
784778

785779
3. Using ``yield from`` for unpacking in synchronous generator expressions, and
786780
disallowing unpacking in asynchronous generator expressions until they
787781
support ``yield from``.
788782

789783
This strategy could possibly reduce friction if asynchronous generator
790784
expressions do gain support for ``yield from`` in the future by making sure
791-
that any decision made at that point would be fully backwards-compatible.
792-
But the utility of unpacking in that context seems to outweigh the potential
793-
downside of a minimally-invasive backwards-incompatible change in the future
794-
if async generator expressions do receive support for ``yield from``.
785+
that any decision made at that point would be fully backwards-compatible,
786+
but in the meantime, it would result in an even bigger discrepancy between
787+
synchronous and asynchronous generator expressions than option 1.
795788

796789
4. Disallowing unpacking in all generator expressions.
797790

798791
This would retain symmetry between the two cases, but with the downside of
799-
losing a very expressive form.
800-
792+
losing an expressive form and reducing symmetry between list/set
793+
comprehensions and generator expressions.
801794

802795
Each of these options (including the one presented in this PEP) has its
803796
benefits and drawbacks, with no option being clearly superior on all fronts.
804-
The semantics proposed in :ref:`pep798-genexpsemantics` represent a reasonable
805-
compromise where unpacking in both synchronous and asynchronous generator
806-
expressions mirrors common ways of writing equivalent generators currently.
807-
Moreover, these subtle differences are unlikely to be impactful for common use
808-
cases (for example, there is no difference for the likely most-common use case
809-
of combining simple collections).
810-
811-
As suggested above, this decision should be revisited in the event that
812-
asynchronous generators receive support for ``yield from`` in the future, in
813-
which case adjusting the semantics of unpacking in async generator expressions
814-
to use ``yield from`` should be considered.
797+
The semantics proposed in :ref:`pep798-genexpsemantics` above represent a
798+
reasonable compromise by allowing exactly the same kind of unpacking in
799+
synchronous and asynchronous generator expressions and retaining an existing
800+
property of generator expressions (that they do not delegate to subgenerators).
801+
802+
This decision should be revisited in the event that asynchronous generators
803+
receive support for ``yield from`` in the future, in which case adjusting the
804+
semantics of unpacking in generator expressions to use ``yield from`` should be
805+
considered.
815806

816807

817808
Concerns and Disadvantages

0 commit comments

Comments
 (0)