A list comprehension in Python is probably one of the first things a Python coder will learn (usually right after arithmetic and boolean logic). But in my own experience, not a lot of people talk about a generator expression. While I have been coding in Python for over 5 years, I personally have learned about it not so long ago.
Instead of using a list comprehension to get something like
[i for i in range(10) if i % 2 == 0]
we can do something like,
(i for i in range(10) if i % 2 == 0)
The latter line of code is a generator expression and returns a generator object which is stored in memory. The generator then only generates the values only when called, so we don’t have to allocate a lot of memory to store something in a list. Compared to a list, a generator takes up much less space. Moreover, it is much more efficient (faster) than building a list.
The above line of code is equivalent to writing a generator function:
def gen(bound): for i in range(bound): if i % 2 == 0: yield i
Some simple examples of what you can use generator expressions for:
sum(1 for i in some_list if condition) # sum conditional elements in a list
for i in irange(1000000): do_something(i) # irange() is a range generator and is more efficient than range() which builds the whole list
For further examples and explanations, see https://wiki.python.org/moin/Generators.