Sign in to your Python Morsels account to save your screencast settings.
Don't have an account yet? Sign up here.
Generators are a type of "iterator".
We have a generator object here (squares
):
>>> numbers = [2, 1, 3, 4, 7]
>>> squares = (n**2 for n in numbers)
>>> squares
<generator object <genexpr> at 0x7f66fcc40f20>
There are two things that you can do with generator objects.
You can pass generators to the built-in next
function to compute just the next item in them:
>>> next(squares)
4
>>> next(squares)
1
Or you can loop over them (with a for
loop, the list constructor, or any other form of looping).
>>> list(squares)
[9, 16, 49]
But you can only loop over them once:
>>> list(squares)
[]
Generators are lazy single-use iterables:
They're lazy because they do work as you loop over them.
They're single-use because they can only be looped over once.
Generators are iterators and iterators are also lazy single-use iterables. In fact, every fact we just saw about generators is also true of iterators.
The same way that you can think of a sequence as a list-like object, you can think of an iterator as a generator-like object. A list is a kind of sequence. A generator is a kind of iterator.
Aside: that style of thinking noted in the last sentence of X-like object is part of Python's philosophy of duck typing.
Generators aren't the only iterator in Python. File objects are also iterators.
Let's take this colors.txt
file:
purple
green
blue
pink
And open it using the built-in open
function to get a file object:
>>> my_file = open('colors.txt')
We can pass this file object to next
to get its next line from disk:
>>> next(my_file)
'purple\n'
>>> next(my_file)
'green\n'
We can also loop over this file object to get each line in the file:
>>> for line in my_file:
... print(line)
...
blue
pink
But those lines are retrieved from disk lazily. Python doesn't get the line until the point that we ask for it.
Almost every looping helper function in Python also returns an iterator. For example enumerate returns an iterator.
We normally use enumerate
by immediately looping over it:
>>> letters = "Python!"
>>> for n, letter in enumerate(letters, start=1):
... print(n, letter)
...
1 P
2 y
3 t
4 h
5 o
6 n
7 !
When we loop over enumerate we'll get a 2-item tuple of numbers counting upward along with each item from the iterable that we passed to enumerate
.
What if instead of immediately looking over the return value of enumerate
, we stored it in a variable?
>>> e = enumerate(letters, start=1)
When we call enumeurate
we'll get an enumerate
object (aside: enumerate
is actually a class):
>>> e
<enumerate object at 0x7f7d3c5afe40>
This enumerate
object acts like an iterator.
We can pass this enumerate
object to the built-in next
function to get just the next item from it:
>>> next(e)
(1, 'P')
>>> next(e)
(2, 'y')
Or we can loop over it fully to get everything within it:
>>> list(e)
[(3, 't'), (4, 'h'), (5, 'o'), (6, 'n'), (7, '!')]
>>> list(e)
[]
But if we loop over this enumerate
object a second time, we'll see there's nothing left in it :
>>> list(e)
[]
An enumerate
object does work as we loop over it.
It delays the work of computing the next item until it's asked for its next item.
The enumerate
function gives us back an iterator.
And so does zip
and so does reversed
and so does everything in Python's itertools
module:
>>> import itertools
Pretty much all of Python's looping helper functions return iterator objects.
Aside: a notable exception is range
, which returns an object that isn't an iterator.
So generators are iterators and iterators are lazy, single-use iterables which do work as you're looping over them. Iterators compute their next value as you ask for it.
Iterators can be passed to the built-in next
function or they can be looped over, but just once (because they're single-use iterables).
You don't learn by watching videos or reading. You learn by writing Python code!
Sign up to Python Morsels to deepen your Python skills single week through hands-on Python exercises. Your weekly practice will be based on your skill level, from novice to advanced.
List comprehensions make new lists. Generator expressions make new generator objects. Generators are iterators, which are lazy single-use iterables. Unlike lists, generators aren't data structures. Instead they do work as you loop over them.
To track your progress on this Python Morsels topic trail, sign in or sign up.
Sign in to your Python Morsels account to track your progress.
Don't have an account yet? Sign up here.