@@ -209,29 +209,36 @@ respectively::
209209 for x in dicts:
210210 new_dict.update(expr)
211211
212+ .. _pep798-genexpsemantics :
212213
213214Semantics: 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+
235242Interaction 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
254262In this example, the subexpression ``(y := [i, i+1]) `` is evaluated exactly
255263three 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
705713a new error message as described above, though it should not be ruled out as a
706714consideration 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+
708775Concerns and Disadvantages
709776==========================
710777
0 commit comments