]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-141984: Reword the Generator expressions section (GH-150518)
authorPetr Viktorin <encukou@gmail.com>
Wed, 10 Jun 2026 14:45:30 +0000 (16:45 +0200)
committerGitHub <noreply@github.com>
Wed, 10 Jun 2026 14:45:30 +0000 (16:45 +0200)
Co-authored-by: Blaise Pabon <blaise@gmail.com>
Co-authored-by: Stan Ulbrych <stan@python.org>
Doc/reference/expressions.rst

index a67e1e100178723be32ce4df53510462c314cd6d..fe38e06cd1dcd2d19e1447df3304ebe50f14986b 100644 (file)
@@ -956,39 +956,100 @@ Generator expressions
    pair: object; generator
    single: () (parentheses); generator expression
 
-A generator expression is a compact generator notation in parentheses:
+The syntax for :dfn:`generator expressions` is the same as for
+list :ref:`comprehensions <comprehensions>`, except that they are enclosed in
+parentheses instead of brackets.
+For example::
 
-.. productionlist:: python-grammar
-   generator_expression: "(" `comprehension` ")"
+   >>> iterator = (x ** 2 for x in range(10))
+   >>> iterator
+   <generator object <genexpr> at ...>
+
+At runtime, a generator expression evaluates to a :term:`generator iterator`
+which yields the same values as the corresponding list comprehension::
+
+   >>> list(iterator)
+   [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::
 
-A generator expression yields a new generator object.  Its syntax is the same as
-for comprehensions, except that it is enclosed in parentheses instead of
-brackets or curly braces.
-
-Variables used in the generator expression are evaluated lazily when the
-:meth:`~generator.__next__` method is called for the generator object (in the same
-fashion as normal generators).  However, the iterable expression in the
-leftmost :keyword:`!for` clause is immediately evaluated, and the
-:term:`iterator` is immediately created for that iterable, so that an error
-produced while creating the iterator will be emitted at the point where the generator expression
-is defined, rather than at the point where the first value is retrieved.
-Subsequent :keyword:`!for` clauses and any filter condition in the leftmost
-:keyword:`!for` clause cannot be evaluated in the enclosing scope as they may
-depend on the values obtained from the leftmost iterable. For example:
-``(x*y for x in range(10) for y in range(x, x+10))``.
-
-The parentheses can be omitted on calls with only one argument.  See section
-:ref:`calls` for details.
+   def make_generator_of_squares(iterator):
+       for x in iterator:
+           yield x ** 2
+
+   make_generator_of_squares(iter(range(10)))
+
+The enclosing parentheses can be omitted in calls when the generator
+expression is the only positional argument and there are no keyword
+arguments.
+See the :ref:`Calls section <calls>` for details.
+For example::
+
+   # The parentheses after `sum` are part of the call syntax:
+   >>> sum(x ** 2 for x in range(10))
+   285
+
+   # The generator needs its own parentheses if it's not the only argument:
+   >>> sum((x ** 2 for x in range(10)), start=1000)
+   1285
+
+The iterable expression in the leftmost :keyword:`!for` clause is
+evaluated immediately, so that an error raised by this expression will be
+emitted at the point where the generator expression is defined,
+rather than at the point where the first value is retrieved::
+
+   >>> (x ** 2 for x in nonexistent_iterable)
+   Traceback (most recent call last):
+     ...
+   NameError: name 'nonexistent_iterable' is not defined
+
+After the expression is evaluated, an iterator is created
+from the result, as if :py:func:`iter` was called on it.
+Any error raised when creating the iterator is also emitted immediately::
+
+   >>> (x ** 2 for x in None)
+   Traceback (most recent call last):
+     ...
+   TypeError: 'NoneType' object is not iterable
+
+All other expressions are evaluated lazily, in the same fashion as normal
+generators (that is, when the iterator is asked to yield a value)::
+
+   >>> iterator = (nonexistent_value for x in range(10))
+   >>> iterator
+   <generator object <genexpr> at ...>
+   >>> list(iterator)
+   Traceback (most recent call last):
+     ...
+   NameError: name 'nonexistent_value' is not defined
+
+::
+
+   >>> iterator = (x * y for x in range(10) for y in nonexistent_iterable)
+   >>> iterator
+   <generator object <genexpr> at ...>
+   >>> list(iterator)
+   Traceback (most recent call last):
+     ...
+   NameError: name 'nonexistent_iterable' is not defined
 
 To avoid interfering with the expected operation of the generator expression
-itself, ``yield`` and ``yield from`` expressions are prohibited in the
-implicitly defined generator.
+itself, ``yield`` and ``yield from`` expressions are prohibited inside
+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`).
+:dfn:`asynchronous generator expression`.
+An asynchronous generator expression returns a new asynchronous generator
+object, which is an asynchronous iterator (see :ref:`async-iterators`).
+
+The formal grammar for generator expressions is:
+
+.. grammar-snippet::
+   :group: python-grammar
+
+   generator_expression: "(" `comprehension` ")"
 
 .. versionadded:: 3.6
    Asynchronous generator expressions were introduced.