Skip to content

Commit bb3706d

Browse files
committed
revert to original proposal's semantics for genexps
1 parent d804372 commit bb3706d

File tree

1 file changed

+52
-44
lines changed

1 file changed

+52
-44
lines changed

peps/pep-0798.rst

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -217,26 +217,33 @@ Semantics: Generator Expressions
217217
Generator expressions using the unpacking syntax should form new generators
218218
producing values from the concatenation of the iterables given by the
219219
expressions. Specifically, the behavior is defined to be equivalent to the
220-
following (though without defining or referencing the looping variable
221-
``i``)::
220+
following::
222221

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

229-
# equivalent to generator = (*expr async for x in ait())
234+
# equivalent to g = (*expr async for x in ait())
230235
async def generator():
231236
async for x in ait():
232237
for i in expr:
233238
yield i
234239

240+
g = generator()
241+
235242
The specifics of these semantics should be revisited in the future,
236243
particularly if async generators receive support for ``yield from`` (in which
237-
case both forms may wish to be changed to make use of ``yield from`` instead of
238-
an explicit loop). See :ref:`pep798-alternativegenexpsemantics` for more
239-
discussion.
244+
case the async variant may wish to be changed to make use of ``yield from``
245+
instead of an explicit loop). See :ref:`pep798-alternativegenexpsemantics` for
246+
more discussion.
240247

241248
Interaction with Assignment Expressions
242249
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -255,8 +262,7 @@ form, ``y`` will be bound in the containing scope instead of locally::
255262

256263
def generator():
257264
for i in (0, 2, 4):
258-
for j in (y := [i, i+1]):
259-
yield j
265+
yield from (y := [i, i+1])
260266

261267
In this example, the subexpression ``(y := [i, i+1])`` is evaluated exactly
262268
three times before the generator is exhausted: just after assigning ``i`` in
@@ -390,11 +396,11 @@ in comprehensions would raise a ``SyntaxError``, or that relied on the
390396
particular phrasing of any of the old error messages being replaced, which we
391397
expect to be rare.
392398

393-
One related concern is that a hypotehtical future decision to change the
394-
semantics of generator expressions to make use of ``yield from`` during
399+
One related concern is that a hypothetical future decision to change the
400+
semantics of async generator expressions to make use of ``yield from`` during
395401
unpacking (delegating to generators that are being unpacked) would be not be
396402
backwards-compatible because it would affect the behavior of the resulting
397-
generators when used with ``.send()``, ``.throw()``, and ``.close()``. See
403+
generators when used with ``.asend()``, ``.athrow()``, and ``.aclose()``. See
398404
:ref:`pep798-alternativegenexpsemantics` for more discussion.
399405

400406
.. _pep798-examples:
@@ -730,37 +736,38 @@ synchronous and asynchronous generator expressions given that async generators
730736
do not support ``yield from`` (see the section of :pep:`525` on Asynchronous
731737
``yield from``).
732738

733-
The core question centered around whether sync and/or async generator
734-
expressions should use ``yield from`` (or an equivalent) when unpacking, as
735-
opposed to an explicit loop. The main difference between these options is
736-
whether the resulting generator delegates to the objects being unpacked (see
737-
:pep:`380`), which would affect the behavior of these generator expressions
738-
when used with ``.send()``, ``.throw()``, and ``.close()``, in the case where
739-
the objects being unpacked are themselves generators, which appears to be a
740-
rare situation.
739+
The core question centered around whether sync and async generator expressions
740+
should use ``yield from`` (or an equivalent) when unpacking, as opposed to an
741+
explicit loop. The main difference between these options is whether the
742+
resulting generator delegates to the objects being unpacked (see :pep:`380`),
743+
which would affect the behavior of these generator expressions when used with
744+
``.send()/.asend()``, ``.throw()/.athrow()``, and ``.close()/.aclose()``, in
745+
the case where the objects being unpacked are themselves generators, which
746+
is unlikely to be a common use case.
741747

742748
Several reasonable options were considered, none of which was a clear winner in
743749
a `poll in the Discourse thread
744-
<https://discuss.python.org/t/pep-798-unpacking-in-comprehensions/99435/33>`__:
750+
<https://discuss.python.org/t/pep-798-unpacking-in-comprehensions/99435/33>`__.
751+
Beyond the proposal outlined above, the following were also considered:
745752

746-
1. Using ``yield from`` for unpacking in synchronous generator expressions but
747-
not in asynchronous generator expressions (as proposed in the original draft
748-
of this PEP).
753+
1. Using explicit loops for both synchronous and asynchronous generator
754+
expressions.
749755

750-
This strategy would have allowed unpacking in generator expressions to
751-
closely mimic a popular way of writing generators that perform this
752-
operation (using ``yield from``), but it would also have created an
753-
asymmetry between synchronous and asynchronous versions, and also between
754-
this new syntax and ``itertools.chain`` and the double-loop version.
756+
This strategy would have resulted in a symmetry between synchronous and
757+
asynchronous generator expressions but would have prevented a
758+
potentially-useful tool by disallowing delegation in the case of synchronous
759+
generator expressions. Moreover, the existing asymmetry between synchronous
760+
and asynchronous generators mitigates concerns about asymmetry of the
761+
unpacking operator within generator expressions.
755762

756763
2. Using ``yield from`` for unpacking in synchronous generator expressions and
757764
mimicking the behavior of ``yield from`` for unpacking in async generator
758765
expressions.
759766

760767
This strategy would also make unpacking in synchronous and asynchronous
761-
generators behave similarly, but it would also be more complex, enough so
762-
that the cost may not be worth the benefit, particularly in the absence of a
763-
compelling practical use case for delegating to subgenerators during
768+
generators behave symmetrically, but it would also be more complex, enough
769+
so that the cost may not be worth the benefit, particularly in the absence
770+
of a compelling practical use case for delegating to subgenerators during
764771
unpacking.
765772

766773
3. Using ``yield from`` for unpacking in synchronous generator expressions, and
@@ -770,25 +777,26 @@ a `poll in the Discourse thread
770777
This strategy could possibly reduce friction if asynchronous generator
771778
expressions do gain support for ``yield from`` in the future by making sure
772779
that any decision made at that point would be fully backwards-compatible,
773-
but in the meantime, it would result in an even bigger discrepancy between
774-
synchronous and asynchronous generator expressions than option 1.
780+
but the utility of unpacking in that context seems to outweigh the potential
781+
downside of a backwards-incompatible change in the future if aync generator
782+
expressions do receive support for ``yield from``.
775783

776784
4. Disallowing unpacking in all generator expressions.
777785

778786
This would retain symmetry between the two cases, but with the downside of
779787
losing a very expressive form.
780788

789+
781790
Each of these options (including the one presented in this PEP) has its
782791
benefits and drawbacks, with no option being clearly superior on all fronts;
783792
but the semantics proposed in :ref:`pep798-genexpsemantics` represent a
784-
reasonable compromise by allowing exactly the same kind of unpacking in
785-
synchronous and asynchronous generator expressions and retaining an existing
786-
property of generator expressions (that they do not
787-
788-
As suggested above, this decision should be revisited in the event that
789-
asynchronous generators receive support for ``yield from`` in the future,
790-
in which case the ability to delegate to subgenerators during unpacking
791-
could be added without significant cost.
793+
reasonable compromise where unpacking in both synchronous and asynchronous
794+
generator expressions mirrors common ways of writing equivalent generators.
795+
796+
As suggested above, though, this decision should be revisited in the event that
797+
asynchronous generators receive support for ``yield from`` in the future, in
798+
which case adjusting the semantics of unpacking in async generator expressions
799+
to use ``yield from`` should be considered.
792800

793801

794802
Concerns and Disadvantages

0 commit comments

Comments
 (0)