diff --git a/Doc/builtins/stdtypes.rst b/Doc/builtins/stdtypes.rst index cf05f947c5908d..16d8aec35dbd02 100644 --- a/Doc/builtins/stdtypes.rst +++ b/Doc/builtins/stdtypes.rst @@ -976,32 +976,84 @@ A function that contains one or more :ref:`yield expressions ` is a :term:`generator function`. For example:: - >>> def count_to_three(): - ... yield 0 - ... yield 1 - ... yield 2 - ... yield 3 + >>> def generator_function(): + ... print("generator starting") + ... yield 123 + ... print("generator at line 4") + ... yield 456 + ... print("generator ending") Generator functions behave as regular :ref:`user-defined functions ` (for example, they have the same attributes), except that calling a generator -function returns a :ref:`generator iterator `:: +function returns a :term:`generator iterator`:: + + >>> generator_iterator = generator_function() + >>> generator_iterator + + +The iterator then allows control over the execution of the underlying function. +Initially, the function is *suspended*. +Execution starts when a :dfn:`consumer` asks the iterator to produce the next +value, for example using the :func:`next` function (which calls +the generator's :meth:`~generator.__next__()` method). +The execution starts at the beginning of the function body +and proceeds to the first :keyword:`!yield` expression. +The expression after the :keyword:`!yield` keyword is evaluated and returned +to the consumer (here, the caller of ``next``):: + + >>> first_value = next(generator_iterator) + generator starting + >>> print(first_value) + 123 + +At this point, execution of the generator function is *suspended* again. +By *suspended*, we mean that all local execution state is retained, including the +current bindings of local variables, the "instruction pointer" (index of +the :keyword:`!yield` expression at which execution is suspended), +the internal evaluation stack, and the state of any exception handling. + +When the iterator is asked to produce another value, execution is *resumed*: +the function proceeds from the :keyword:`!yield` expression where it was +suspended and continues until the next :keyword:`!yield` expression +is encountered:: + + >>> second_value = next(generator_iterator) + generator at line 4 + >>> print(second_value) + 456 + +If the generator function returns before a :keyword:`!yield` expression is +encountered (either with a :keyword:`return` statement, or implicitly at the +end of the function), the generator iterator becomes :term:`exhausted`. +All further attempts to get the next value from it will raise +a :exc:`StopIteration` exception:: + + >>> third_value = next(generator_iterator) + Traceback (most recent call last): + ... + StopIteration + >>> third_value = next(generator_iterator) + Traceback (most recent call last): + ... + StopIteration - >>> count_to_three() - +Commonly, an iterator is consumed via a :keyword:`for` loop, which implicitly +calls :func:`next` repeatedly, and handles :exc:`StopIteration`:: -Iterating a generator iterator executes code of the underlying -generator function, producing each :keyword:`yield`\ed value in turn:: + >>> for value in generator_function(): + ... print("value from generator:", value) + generator starting + value from generator: 123 + generator at line 4 + value from generator: 456 + generator ending - >>> for number in count_to_three(): - ... print(number) - 0 - 1 - 2 - 3 +.. note:: - >>> list(count_to_three()) - [0, 1, 2, 3] + In this example, the ``generator_function()`` call creates a new iterator + with its own local execution state. + The exhausted ``generator_iterator`` from earlier examples is not reused. One common use for generator functions is implementing the :meth:`~object.__iter__` method of custom iterable objects. @@ -1015,12 +1067,71 @@ For example:: >>> list(CardDeck()) ['three of clubs', 'ace of hearts'] +.. seealso:: + + :pep:`255` - Simple Generators + The proposal for adding generators and the :keyword:`yield` statement to Python. + +Generator finalization +^^^^^^^^^^^^^^^^^^^^^^ + +If a generator iterator is not resumed before it is destroyed +(:term:`garbage collected `), +its :meth:`~generator.close` method will be called, +raising a :exc:`GeneratorExit` at the :keyword:`!yield` expression where the +function is suspended. +If that :keyword:`!yield` expression is in a :keyword:`try` construct, +pending :keyword:`finally` clauses execute. + +.. admonition:: CPython implementation detail + + In the following example, ``iterator`` is garbage collected immediately + after the ``del iterator`` statement, triggering the ``finally`` block:: + + >>> def gen(): + ... try: + ... yield 123 + ... finally: + ... print('shutting down') + ... + >>> iterator = gen() + >>> next(iterator) + 123 + >>> del iterator + shutting down + + On non-CPython implementations, or future versions of CPython, + garbage collection (and thus printing the message) may happen at any + later point, or even not at all. + +After :meth:`~generator.close` raises :exc:`!GeneratorExit`, +the underlying function must not yield. +If it does, :exc:`RuntimeError` is raised instead of suspending the function:: + + >>> def gen(): + ... try: + ... yield 123 + ... except GeneratorExit: + ... pass # ignore the exception + ... yield 456 + ... + >>> iterator = gen() + >>> next(iterator) + 123 + >>> iterator.close() + Traceback (most recent call last): + File "", line 1, in + iterator.close() + ~~~~~~~~^^ + RuntimeError: generator ignored GeneratorExit + + .. index:: pair: object; generator .. _generator-methods: -Generator iterators -^^^^^^^^^^^^^^^^^^^ +Generator iterator methods +^^^^^^^^^^^^^^^^^^^^^^^^^^ Generator iterators implement the :ref:`iterator protocol `. @@ -1032,20 +1143,19 @@ Iterating them drives execution of the underlying generator function. Starts the execution of a generator function or resumes it at the :ref:`yield expression ` where the function is currently suspended. - When a generator function is resumed with a :meth:`~generator.__next__` - method, the current yield expression always evaluates to :const:`None`. - The execution then continues to the next yield expression, where the - generator is suspended again, and the value of the expression after the - :keyword:`yield` keyword is returned to :meth:`~generator.__next__`'s - caller. + The :keyword:`!yield` expression (if any) evaluates to :const:`!None`. + The execution then continues to the next :keyword:`!yield` expression, + where the generator is suspended again, and the value of the expression + after the :keyword:`!yield` keyword is returned to + :meth:`!__next__`'s caller. If the generator exits without yielding another value, - :meth:`~generator.__next__` raises a :exc:`StopIteration` exception, + :meth:`!__next__` raises a :exc:`StopIteration` exception, signalling that iteration has completed. This method is normally called implicitly, for example by a :keyword:`for` loop, or by the built-in :func:`next` function. -Generator iterators have a few more methods than generic iterators, which +Generator iterators have a few more methods than generic iterators. These can be used to control the execution of the underlying generator function: .. method:: generator.send(value) @@ -1057,8 +1167,8 @@ can be used to control the execution of the underlying generator function: the underlying function and either returns the next yielded value or raises :exc:`StopIteration`. - When :meth:`send` is called to start the generator, it must be called - with :const:`None` as the argument, because there is no current yield + When :meth:`!send` is called to start the generator, it must be called + with :const:`!None` as the argument, because there is no current yield expression that could receive the value. @@ -1073,10 +1183,9 @@ can be used to control the execution of the underlying generator function: If the generator function does not catch the passed-in exception, or raises a different exception, then that exception propagates to the caller. - When :meth:`throw` is called to start the generator, the generator - immediately exits (that is, subsequent calls to :meth:`~generator.__next__` - will raise :exc:`StopIteration`) and the thrown exception is propagated to - :meth:`throw`'s caller. + When :meth:`throw` is called to start the generator, the generator is + immediately :term:`exhausted`, the underlying function is not executed, + and the thrown exception is propagated to :meth:`throw`'s caller. In typical use, this is called with a single argument, an exception instance, similar to the way the :keyword:`raise` keyword is used. @@ -1102,9 +1211,9 @@ can be used to control the execution of the underlying generator function: Raises a :exc:`GeneratorExit` exception at the point where the generator function is currently suspended (equivalent to calling ``throw(GeneratorExit)``). - If the generator function has already exited (due to an exception or - normal return), or raises :exc:`GeneratorExit` (by not catching the - exception), :meth:`close` returns :const:`None`. + Then, if the generator function has already exited (due to an exception or + normal return), or raises :exc:`GeneratorExit` (including by not catching + the exception), :meth:`close` returns :const:`None`. If the generator yields a value, a :exc:`RuntimeError` is raised. If the generator raises any other exception, it is propagated to the caller. If a generator returns a value upon being closed, that value is returned @@ -1125,6 +1234,12 @@ Calling any of the generator methods (:meth:`~generator.__next__`, while one of these methods is already executing raises a :exc:`ValueError` exception. +.. seealso:: + + :pep:`342` - Coroutines via Enhanced Generators + The proposal to add :meth:`!send`, :meth:`!throw` and :meth:`!close`, + making generators usable as simple coroutines. + .. index:: pair: object; asynchronous-generator .. _asynchronous-generator-methods: diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 68bc0560911161..2accabf4195f81 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -649,7 +649,7 @@ Glossary collector that is able to detect and break reference cycles. The garbage collector can be controlled using the :mod:`gc` module. - .. index:: single: generator + .. index:: single: generator generator Informally used to mean either a :term:`generator function` or a @@ -657,7 +657,7 @@ Glossary :term:`generator function` and :term:`generator iterator` are uncommon in practice; "generator" alone is almost always sufficient. - .. index:: single: generator function + .. index:: single: generator function generator function A function which returns a :term:`generator` object. It looks like a @@ -680,7 +680,7 @@ Glossary :meth:`~generator.throw` method to raise an exception at the point where the generator was paused. See :ref:`generator-methods`. - .. index:: single: generator expression + .. index:: single: generator expression generator expression An :term:`expression` that returns an :term:`iterator`. It looks like a normal expression diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 2e0b6621eb11c8..5ee5edd879bb2d 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -974,7 +974,7 @@ which yields the same values as the corresponding list comprehension:: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] Thus, the example above is roughly equivalent to defining and calling -the following generator function:: +the following :term:`generator function`:: def make_generator_of_squares(iterator): for x in iterator: @@ -1043,8 +1043,9 @@ the implicitly nested scope. If a generator expression contains either :keyword:`!async for` clauses or :keyword:`await` expressions it is called an :dfn:`asynchronous generator expression`. -An asynchronous generator expression returns a new asynchronous generator -object, which is an asynchronous iterator (see :ref:`async-iterators`). +An asynchronous generator expression returns a new +:term:`asynchronous generator iterator`, which is an +:ref:`asynchronous iterator `. The formal grammar for generator expressions is: @@ -1065,124 +1066,310 @@ The formal grammar for generator expressions is: ``yield`` and ``yield from`` prohibited in the implicitly nested scope. -.. _yieldexpr: - -Yield expressions ------------------ - .. index:: pair: keyword; yield pair: keyword; from pair: yield; expression pair: generator; function -.. productionlist:: python-grammar - yield_atom: "(" `yield_expression` ")" - yield_from: "yield" "from" `expression` - yield_expression: "yield" `yield_list` | `yield_from` +.. _yield: +.. _yieldexpr: -The yield expression is used when defining a :term:`generator` function -or an :term:`asynchronous generator` function and -thus can only be used in the body of a function definition. Using a yield -expression in a function's body causes that function to be a generator function, -and using it in an :keyword:`async def` function's body causes that -coroutine function to be an asynchronous generator function. For example:: +.. index:: + single: generator; function + single: generator; iterator + single: function; generator + pair: exception; StopIteration + +:keyword:`!yield` expressions +----------------------------- + +.. This is a high-level conceptual overview. + The linked ":ref:`generator-types`" section (in the Builtins chapter) + has the details on runtime behaviour; + further sections *here* give details on the syntax. + +The :keyword:`!yield` keyword introduces a :dfn:`yield expression`. +:keyword:`!yield` expressions may only occur inside function definitions; +see :ref:`yield-expression-placement` for details. + +A function that contains one or more yield expressions +is a :term:`generator function`. +At runtime, calling such a function does not run its code immediately, but instead +returns a :term:`generator iterator` which controls execution of the underlying +function. + +The :keyword:`!yield` keyword is usually followed by +an :ref:`expression `. +At runtime, each time the generator iterator is asked to produce the next +value, execution proceeds to the next :keyword:`!yield` keyword, its expression +is evaluated, and the result is used as the iterator's next value. - def gen(): # defines a generator function - yield 123 +For example:: - async def agen(): # defines an asynchronous generator function - yield 123 + >>> def count_to_three(): + ... """A generator that produces the numbers 0 to 3""" + ... yield 0 + ... yield 1 + ... yield 2 + ... yield 1 + 2 + + >>> for number in count_to_three(): + ... print("got:", number) + got: 0 + got: 1 + got: 2 + got: 3 + +See :ref:`generator-types` for details on runtime behavior and +ways to control the execution of the underlying function. + +If the underlying function is defined using ``async def``, it becomes an +:term:`asynchronous generator function` instead. +For example:: -Due to their side effects on the containing scope, ``yield`` expressions -are not permitted as part of the implicitly defined scopes used to -implement comprehensions and generator expressions. + async def agen(): + """An asynchronous generator that produces a single value""" + yield 123 -.. versionchanged:: 3.8 - Yield expressions prohibited in the implicitly nested scopes used to - implement comprehensions and generator expressions. +Asynchronous generator functions are described separately +:ref:`below `. + + +:keyword:`yield` without a value +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the expression after a :keyword:`yield` keyword is omitted, the generator +iterator produces ``None``:: + + >>> def generator(): + ... yield 123 + ... yield + + >>> for value in generator(): + ... print("got:", value) + got: 123 + got: None + + +Using :keyword:`!yield` as an expression +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Usually, :keyword:`!yield` expressions are used alone on a line, +as in the examples above. +However, like other expressions, they can also be used in other contexts, +such as assignments and as parts of more complex expressions. + +.. seealso:: + + This feature was originally added in :pep:`342` as a way to implement + coroutines. + While it still works, :term:`coroutine functions ` + (using :keyword:`async` and :keyword:`await`) offer a better way to + do this. + +Like other expressions, :keyword:`!yield` expressions +can be used in assignments:: + + >>> def generator_function(): + ... value = yield 123 + ... print("value of yield is", value) + +At runtime, when using the basic iteration protocol (:keyword:`for` or +:func:`next`), the value of a :keyword:`!yield` expression is ``None``:: + + >>> for value in generator_function(): + ... pass + value of yield is: None + +A different value can be given by using the generator iterator's +:meth:`~generator.send` method instead of :keyword:`!for` or :func:`!next`:: + + >>> generator_iterator = generator_function() + >>> next(generator_iterator) + 123 + >>> # The generator is now suspended at the "yield 123" expression. + >>> # Provide a value for it: + >>> generator_iterator.send("different") + value of yield is: different + + Traceback (most recent call last): + StopIteration + +.. _yield-expression-parentheses: + +A :keyword:`!yield` expression can also appear as part of a larger expression. +In this case, it must be enclosed in parentheses. +For example:: + + >>> def adding_generator(): + ... sum_of_yields = (yield "first number") + (yield "second number") + ... print("The sum is:", sum_of_yields) + + >>> iterator = adding_generator() + >>> next(iterator) + "first number" + >>> iterator.send(1) + "second number" + >>> iterator.send(2) + The sum is: 3 + + Traceback (most recent call last): + StopIteration + +Parentheses around :keyword:`yield` expressions are also required in other +contexts, such as :keyword:`if` statement conditions. +They can be only omitted when the :keyword:`yield` +expression is: + +- an :ref:`expression statement `'s only expression, or +- the only expression on the right-hand side of an + :ref:`assignment statement `. -Generator functions are described below, while asynchronous generator -functions are described separately in section -:ref:`asynchronous-generator-functions`. - -When a generator function is called, it returns an iterator known as a -generator. That generator then controls the execution of the generator -function. The execution starts when one of the generator's methods is called. -At that time, the execution proceeds to the first yield expression, where it is -suspended again, returning the value of :token:`~python-grammar:yield_list` -to the generator's caller, -or ``None`` if :token:`~python-grammar:yield_list` is omitted. -By suspended, we mean that all local state is -retained, including the current bindings of local variables, the instruction -pointer, the internal evaluation stack, and the state of any exception handling. -When the execution is resumed by calling one of the generator's methods, the -function can proceed exactly as if the yield expression were just another -external call. The value of the yield expression after resuming depends on the -method which resumed the execution. If :meth:`~generator.__next__` is used -(typically via either a :keyword:`for` or the :func:`next` builtin) then the -result is :const:`None`. Otherwise, if :meth:`~generator.send` is used, then -the result will be the value passed in to that method. - -.. index:: single: coroutine - -All of this makes generator functions quite similar to coroutines; they yield -multiple times, they have more than one entry point and their execution can be -suspended. The only difference is that a generator function cannot control -where the execution should continue after it yields; the control is always -transferred to the generator's caller. - -Yield expressions are allowed anywhere in a :keyword:`try` construct. If the -generator is not resumed before it is -finalized (by reaching a zero reference count or by being garbage collected), -the generator-iterator's :meth:`~generator.close` method will be called, -allowing any pending :keyword:`finally` clauses to execute. .. index:: single: from; yield from expression -When ``yield from `` is used, the supplied expression must be an -iterable. The values produced by iterating that iterable are passed directly -to the caller of the current generator's methods. Any values passed in with -:meth:`~generator.send` and any exceptions passed in with -:meth:`~generator.throw` are passed to the underlying iterator if it has the -appropriate methods. If this is not the case, then :meth:`~generator.send` -will raise :exc:`AttributeError` or :exc:`TypeError`, while -:meth:`~generator.throw` will just raise the passed in exception immediately. - -When the underlying iterator is complete, the :attr:`~StopIteration.value` +:keyword:`!yield from` expressions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +An expression of the form :samp:`yield from {subiterable}` is +a :keyword:`!yield from` expression. + +Like a :keyword:`yield` expression, a :keyword:`!yield from` expression +is only allowed in functions and turns the enclosing function into a generator. +It must be parenthesized in some contexts; :ref:`the rules ` +for parentheses are the same as for :keyword:`!yield` expressions. + +At runtime, *subiterable* -- the expression after the :keyword:`!yield from` +keywords -- must evaluate to an iterable. + +For simple iterables, :samp:`yield from {subiterable}` is essentially +a shortened form of :samp:`for item in {subiterable}: yield item`. +Values produced by *subiterable* are passed directly +to the consumer of the current generator. For example:: + + >>> hand_movements = ['put hands up', 'put hands down'] + + >>> def dance(): + ... yield from hand_movements + ... yield 'turn around' + ... yield from hand_movements + + >>> for move in dance(): + ... print(move) + put hands up + put hands down + turn around + put hands up + put hands down + +However, unlike an ordinary loop, :keyword:`!yield from` delegates the +entire generator protocol. +Any values passed in with :meth:`~generator.send` and any exceptions passed +in with :meth:`~generator.throw` and :func:`~generator.close` are passed +to the subiterator if it has the appropriate methods. +If this is not the case, then :meth:`~generator.send` will raise +:exc:`AttributeError` or :exc:`TypeError`, while :meth:`~generator.throw` +and :func:`~generator.close` will raise the passed in exception immediately. + +When a :keyword:`!yield from` expression's *subiterable* is complete, +the :attr:`~StopIteration.value` attribute of the raised :exc:`StopIteration` instance becomes the value of -the yield expression. It can be either set explicitly when raising -:exc:`StopIteration`, or automatically when the subiterator is a generator -(by returning a value from the subgenerator). +the :keyword:`!yield from` expression. +The value can be either set explicitly when raising +:exc:`StopIteration`, or, when the subiterator is a generator, +by returning a value from the subgenerator's underlying function: -.. versionchanged:: 3.3 - Added ``yield from `` to delegate control flow to a subiterator. +.. code-block:: pycon -The parentheses may be omitted when the yield expression is the sole expression -on the right hand side of an assignment statement. + >>> def subgen(): + ... yield 1 + ... yield 2 + ... return 3 -.. seealso:: + >>> def maingen(): + ... result = yield from subgen() + ... yield ('result:', result) + + >>> list(maingen()) + [1, 2, ('result:', 3)] - :pep:`255` - Simple Generators - The proposal for adding generators and the :keyword:`yield` statement to Python. +.. versionchanged:: 3.3 + Added ``yield from `` to delegate control flow to a subiterator. - :pep:`342` - Coroutines via Enhanced Generators - The proposal to enhance the API and syntax of generators, making them - usable as simple coroutines. +.. seealso:: :pep:`380` - Syntax for Delegating to a Subgenerator The proposal to introduce the :token:`~python-grammar:yield_from` syntax, making delegation to subgenerators easy. - :pep:`525` - Asynchronous Generators - The proposal that expanded on :pep:`492` by adding generator capabilities to - coroutine functions. +.. _yield-expression-placement: + +Where :keyword:`yield` expressions are allowed +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A :keyword:`yield` expression can only be used in the body of a +function definition. +More precisely, it can only be used when the closest enclosing *block* +is a function definition, not a module-level or class block. +(See :ref:`prog_structure` for an explanation of blocks.) +For example:: + + class C: + # yield is not allowed here + + def foo(self): + yield 1 # this makes `foo` a generator function + + class NestedClass: + # yield is not allowed here + pass + + def nested_function(): + # this function is *not* a generator + + def double_nested(): + # this `yield` makes `double_nested` (but + # not `nested_function`) a generator function + yield 2 + +Due to their side effects on the containing scope, ``yield`` expressions +are not permitted as part of the implicitly defined scopes used to +implement :ref:`comprehensions ` and +:ref:`generator expressions `. + +For :keyword:`!yield from` expressions, the same restrictions apply. + +.. versionchanged:: 3.8 + Yield expressions prohibited in the implicitly nested scopes used to + implement comprehensions and generator expressions. + + +Formal grammar of :keyword:`!yield` expressions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The formal grammar of :keyword:`!yield` and :keyword:`!yield from` +expressions is: + +.. grammar-snippet:: + :group: python-grammar + + yield_atom: "(" `yield_expression` ")" + + yield_expression: + | 'yield' 'from' `expression` + | 'yield' [`starred_expression_list`] + +Note that ``yield_expression``, the unparenthesized rule, also appears in +:token:`~python-grammar:expression_stmt` and +:token:`~python-grammar:assignment_stmt`. + .. index:: single: yield; examples -Examples -^^^^^^^^ +Examples of :keyword:`!yield` expressions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here is a simple example that demonstrates the behavior of generators and generator functions:: @@ -1211,8 +1398,36 @@ generator functions:: >>> generator.close() Don't forget to clean up when 'close()' is called. -For examples using ``yield from``, see :ref:`pep-380` in "What's New in -Python." +Here is a more complex example that demonstrates ``yield from``, including +delegation of sent values and receiving the value returned by +a subgenerator function:: + + >>> def accumulate(): + ... tally = 0 + ... while True: + ... next = yield + ... if next is None: + ... return tally + ... tally += next + ... + >>> def gather_tallies(tallies): + ... while True: + ... tally = yield from accumulate() + ... tallies.append(tally) + ... + >>> tallies = [] + >>> acc = gather_tallies(tallies) + >>> next(acc) # Ensure the accumulator is ready to accept values + >>> for i in range(4): + ... acc.send(i) + ... + >>> acc.send(None) # Finish the first tally + >>> for i in range(5): + ... acc.send(i) + ... + >>> acc.send(None) # Finish the second tally + >>> tallies + [6, 10] .. _asynchronous-generator-functions: @@ -1233,7 +1448,7 @@ how a generator object would be used in a :keyword:`for` statement. Calling one of the asynchronous generator's methods returns an :term:`awaitable` object, and the execution starts when this object is awaited on. At that time, the execution proceeds to the first yield expression, where it is suspended -again, returning the value of :token:`~python-grammar:yield_list` to the +again, returning the value of the expression after :keyword:`!yield` to the awaiting coroutine. As with a generator, suspension means that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. @@ -1278,6 +1493,12 @@ of a *finalizer* method see the implementation of The expression ``yield from `` is a syntax error when used in an asynchronous generator function. +.. seealso:: + + :pep:`525` - Asynchronous Generators + The proposal that expanded on :pep:`492` by adding generator capabilities to + coroutine functions. + .. _primaries: Primaries @@ -2475,7 +2696,6 @@ Expression lists flexible_expression_list: `flexible_expression` ("," `flexible_expression`)* [","] starred_expression_list: `starred_expression` ("," `starred_expression`)* [","] expression_list: `expression` ("," `expression`)* [","] - yield_list: `expression_list` | `starred_expression` "," [`starred_expression_list`] .. index:: pair: object; tuple diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index a964b43ebee174..74d71da9e68d51 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -20,7 +20,6 @@ simple statements is: : | `pass_stmt` : | `del_stmt` : | `return_stmt` - : | `yield_stmt` : | `raise_stmt` : | `break_stmt` : | `continue_stmt` @@ -32,6 +31,7 @@ simple statements is: .. _exprstmts: +.. _yield statements: Expression statements ===================== @@ -44,12 +44,7 @@ Expression statements Expression statements are used (mostly interactively) to compute and write a value, or (usually) to call a procedure (a function that returns no meaningful result; in Python, procedures return the value ``None``). Other uses of -expression statements are allowed and occasionally useful. The syntax for an -expression statement is: - -.. productionlist:: python-grammar - expression_stmt: `starred_expression` - +expression statements are allowed and occasionally useful. An expression statement evaluates the expression list (which may be a single expression). @@ -67,6 +62,24 @@ using the built-in :func:`repr` function and the resulting string is written to standard output on a line by itself (except if the result is ``None``, so that procedure calls do not cause any output.) +The formal grammar for an expression statement is: + +.. index:: pair: statement; yield + +.. grammar-snippet:: + :group: python-grammar + + expression_stmt: `starred_expression` | `yield_expression` + +.. note:: + + The formal grammar includes a special case for a single :keyword:`!yield` + expression, without parentheses around it. + This is sometimes called a :dfn:`yield statement`. + For complete documentation of :keyword:`!yield` semantics, see + the :ref:`yieldexpr` section. + + .. _assignment: Assignment statements @@ -512,42 +525,6 @@ indicates that the asynchronous generator is done and will cause :exc:`StopAsyncIteration` to be raised. A non-empty :keyword:`!return` statement is a syntax error in an asynchronous generator function. -.. _yield: - -The :keyword:`!yield` statement -=============================== - -.. index:: - pair: statement; yield - single: generator; function - single: generator; iterator - single: function; generator - pair: exception; StopIteration - -.. productionlist:: python-grammar - yield_stmt: `yield_expression` - -A :keyword:`yield` statement is semantically equivalent to a :ref:`yield -expression `. The ``yield`` statement can be used to omit the -parentheses that would otherwise be required in the equivalent yield expression -statement. For example, the yield statements :: - - yield - yield from - -are equivalent to the yield expression statements :: - - (yield ) - (yield from ) - -Yield expressions and statements are only used when defining a :term:`generator` -function, and are only used in the body of the generator function. Using :keyword:`yield` -in a function definition is sufficient to cause that definition to create a -generator function instead of a normal function. - -For full details of :keyword:`yield` semantics, refer to the -:ref:`yieldexpr` section. - .. _raise: The :keyword:`!raise` statement