Difference between generator expression and generator function

2024/10/2 6:29:19

Is there any difference — performance or otherwise — between generator expressions and generator functions?

In [1]: def f():...:     yield from range(4)...:In [2]: def g():...:     return (i for i in range(4))...:In [3]: f()
Out[3]: <generator object f at 0x109902550>In [4]: list(f())
Out[4]: [0, 1, 2, 3]In [5]: list(g())
Out[5]: [0, 1, 2, 3]In [6]: g()
Out[6]: <generator object <genexpr> at 0x1099056e0>

I'm asking because I want to decide how I should decide between using the two. Sometimes generator functions are clearer and then the choice is clear. I am asking about those times when code clarity does not make one choice obvious.

Answer

The functions you provided have completely different semantics in the general case.

The first one, with yield from, passes the control to the iterable. This means that calls to send() and throw() during the iteration will be handled by the iterable and not by the function you are defining.

The second function only iterates over the elements of the iterable, and it will handle all the calls to send() and throw(). To see the difference check this code:

In [8]: def action():...:     try:...:         for el in range(4):...:             yield el...:     except ValueError:...:         yield -1...:         In [9]: def f():...:     yield from action()...:     In [10]: def g():...:     return (el for el in action())...: In [11]: x = f()In [12]: next(x)
Out[12]: 0In [13]: x.throw(ValueError())
Out[13]: -1In [14]: next(x)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-14-5e4e57af3a97> in <module>()
----> 1 next(x)StopIteration: In [15]: x = g()In [16]: next(x)
Out[16]: 0In [17]: x.throw(ValueError())
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-17-1006c792356f> in <module>()
----> 1 x.throw(ValueError())<ipython-input-10-f156e9011f2f> in <genexpr>(.0)1 def g():
----> 2     return (el for el in action())3 ValueError: 

In fact, due to this reason, yield from probably has a higher overhead than the genexp, even though it is probably irrelevant.

Use yield from only when the above behaviour is what you want or if you are iterating over a simple iterable that is not a generator (so that yield from is equivalent to a loop + simple yields).

Stylistically speaking I'd prefer:

def h():for el in range(4):yield el

Instead of returning a genexp or using yield from when dealing with generators.

In fact the code used by the generator to perform the iteration is almost identical to the above function:

In [22]: dis.dis((i for i in range(4)).gi_code)1           0 LOAD_FAST                0 (.0)>>    3 FOR_ITER                11 (to 17)6 STORE_FAST               1 (i)9 LOAD_FAST                1 (i)12 YIELD_VALUE13 POP_TOP14 JUMP_ABSOLUTE            3>>   17 LOAD_CONST               0 (None)20 RETURN_VALUE

As you can see it does a FOR_ITER + YIELD_VALUE. note that the argument (.0), is iter(range(4)). The bytecode of the function also contains the calls to LOAD_GLOBAL and GET_ITER that are required to lookup range and obtain its iterable. However this actions must be performed by the genexp too, just not inside its code but before calling it.

https://en.xdnf.cn/q/70880.html

Related Q&A

Django performance testing suite thatll report on metrics (db queries etc.)

I have a complex Django web application that has many person-years of work put into it. It might need optimisation sometime. There are several common operation/flows that I could script with (say) djan…

dev_appserver.py Opens a Text File, Does Not Deploy

It works fine on my other computer, but after setting up Google App Engine and creating the main.py and app.yaml files, I run dev_appserver.py app.yaml in Windows command prompt and instead of deployin…

How to pass a list from a view to template in django

I am trying pass to list from a view to template in Django.In my file wiew.py I define the view named hour # This Python file uses the following encoding: utf-8from django.shortcuts import render from …

Probing/sampling/interpolating VTK data using python TVTK or MayaVi

I would like to visualise a VTK data file (OpenFOAM output) using python. The plot I would like to make is a 1-d line plot of a quantity between two endpoints. To do so, the unstructured data should be…

Make Sphinx generate RST class documentation from pydoc

Im currently migrating all existing (incomplete) documentation to Sphinx.The problem is that the documentation uses Python docstrings (the module is written in C, but it probably does not matter) and t…

inspect.getfile () vs inspect.getsourcefile()

I was just going through the inspect module docs.What exactly is the difference between:inspect.getfile()andinspect.getsourcefile()I get exactly the same file path (of the module) for both.

Get a row of data in pandas as a dict

To get a row of data in pandas by index I can do:df.loc[100].tolist()Is there a way to get that row of data as a dict, other than doing:dict(zip(df.columns.tolist(),df.loc[100], tolist() ))

Find duplicate records in large text file

Im on a linux machine (Redhat) and I have an 11GB text file. Each line in the text file contains data for a single record and the first n characters of the line contains a unique identifier for the rec…

Select the first item from a drop down by index is not working. Unbound method select_by_index

I am trying to click the first item from a drop down. I want to use its index value because the value could be different each time. I only need to select the 1st item in the drop down for this particul…

finding the app window currently in focus on Mac OSX

I am writing a desktop usage statistics app. It runs a background daemon which wakes up at regular intervals, finds the name of the application window currently in focus and logs that data in database.…