Skip to content

Commit a054b42

Browse files
committed
change recommended semantics of generator expressions, add alternatives to rejected ideas
1 parent af559b7 commit a054b42

File tree

1 file changed

+79
-12
lines changed

1 file changed

+79
-12
lines changed

peps/pep-0798.rst

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -209,29 +209,36 @@ respectively::
209209
for x in dicts:
210210
new_dict.update(expr)
211211

212+
.. _pep798-genexpsemantics:
212213

213214
Semantics: Generator Expressions
214215
--------------------------------
215216

216-
A generator expression ``(*expr for x in it)`` forms a generator producing
217-
values from the concatenation of the iterables given by the expressions.
218-
Specifically, the behavior is defined to be equivalent to the following
219-
generator::
217+
Generator expressions using the unpacking syntax should form new generators
218+
producing values from the concatenation of the iterables given by the
219+
expressions. Specifically, the behavior is defined to be equivalent to the
220+
following (though without defining for referencing the looping variable
221+
``i``)::
220222

223+
# equivalent to generator = (*expr for x in it)
221224
def generator():
222225
for x in it:
223-
yield from expr
224-
225-
Since ``yield from`` is not allowed inside of async generators (see the section
226-
of :pep:`525` on Asynchronous ``yield from``), the equivalent for ``(*expr
227-
async for x in ait())`` is more like the following (though of course this new
228-
form should not define or reference the looping variable ``i``)::
226+
for i in expr:
227+
yield i
229228

229+
# equivalent to generator = (*expr for x in ait())
230230
async def generator():
231231
async for x in ait():
232232
for i in expr:
233233
yield i
234234

235+
236+
The specifics of these semantics should be revisited in the future,
237+
particularly if async generators receive support for ``yield from`` (in which
238+
case both forms may wish to be changed to make use of ``yield from`` instead of
239+
an explicit loop). See :ref:`pep798-alternativegenexpsemantics` for more
240+
discussion.
241+
235242
Interaction with Assignment Expressions
236243
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
237244

@@ -249,7 +256,8 @@ form, ``y`` will be bound in the containing scope instead of locally::
249256

250257
def generator():
251258
for i in (0, 2, 4):
252-
yield from (y := [i, i+1])
259+
for j in (y := [i, i+1]):
260+
yield j
253261

254262
In this example, the subexpression ``(y := [i, i+1])`` is evaluated exactly
255263
three times before the generator is exhausted: just after assigning ``i`` in
@@ -577,7 +585,7 @@ expressions that involve unpacking::
577585
yield expr
578586
g = generator()
579587

580-
# equivalent to g = (*expr for x in it)
588+
# roughly equivalent to g = (*expr for x in it)
581589
def generator():
582590
for x in it:
583591
yield from expr
@@ -705,6 +713,65 @@ PEP. As such, these forms should continue to raise a ``SyntaxError``, but with
705713
a new error message as described above, though it should not be ruled out as a
706714
consideration for future proposals.
707715

716+
.. _pep798-alternativegenexpsemantics:
717+
718+
Alternative Generator Expression Semantics
719+
------------------------------------------
720+
721+
Another point of discussion centered around the semantics of unpacking in
722+
generator expressions, particularly the relationship between the semantics of
723+
synchronous and asynchronous generator expressions given that the latter do not
724+
support ``yield from`` (see the section of :pep:`525` on Asynchronous ``yield
725+
from``).
726+
727+
Several reasonable options were considered, none of which was a clear winner in
728+
a `poll in the Discourse thread
729+
<https://discuss.python.org/t/pep-798-unpacking-in-comprehensions/99435/33>`__:
730+
731+
1. Using ``yield from`` for unpacking in synchronous generator expressions but
732+
not in asynchronous generator expressions (as proposed in the original draft
733+
of this PEP).
734+
735+
This strategy would have allowed unpacking in generator expressions to
736+
closely mimic a popular way of writing generators that perform this
737+
operation (using ``yield from``), but it would also have created an
738+
asymmetry between synchronous and asynchronous versions, and also between
739+
this new syntax and ``itertools.chain`` and the double-loop version.
740+
741+
2. Using ``yield from`` for unpacking in synchronous generator expressions and
742+
mimicking the behavior of ``yield from`` for unpacking in async generator
743+
expressions.
744+
745+
This strategy would also make unpacking in synchronous and asynchronous
746+
generators behave similarly, but it would also be more complex, enough so
747+
that the cost may not be worth the benefit, particularly in the absence of a
748+
compelling practical use case for delegating to subgenerators during
749+
unpacking.
750+
751+
3. Disallowing unpacking in asynchronous generator expressions until they
752+
support ``yield from``.
753+
754+
This strategy could possibly reduce friction if asynchronous generator
755+
expressions do gain support for ``yield from`` in the future, but in the
756+
meantime, it would result in an even bigger discrepancy between synchronous
757+
and asynchronous generator expressions than option 1.
758+
759+
4. Disallowing unpacking in all generator expressions.
760+
761+
This would retain symmetry between the two cases, but with the downside of
762+
losing a very expressive form.
763+
764+
Each of these options (including the one presented in this PEP) has its
765+
benefits and drawbacks, with no option being clearly superior on all fronts;
766+
but the semantics proposed in :ref:`pep798-genexpsemantics` represent a
767+
reasonable compromise.
768+
769+
As suggested above, this decision should be revisited in the event that
770+
asynchronous generators receive support for ``yield from`` in the future,
771+
in which case the ability to delegate to subgenerators during unpacking
772+
could be added without significant cost.
773+
774+
708775
Concerns and Disadvantages
709776
==========================
710777

0 commit comments

Comments
 (0)