Professional Documents
Culture Documents
Python Generators: What Is A Generator?
Python Generators: What Is A Generator?
Python Generators: What Is A Generator?
What is a Generator?
A Python generator (https://wiki.python.org/moin/Generators) is a function that
produces a sequence of results. It works by maintaining its local state, so that the
function can resume again exactly where it left off when called subsequent times.
Thus, you can think of a generator as something like a powerful iterator.
The state of the function is maintained through the use of the keyword yield ,
which has the following syntax:
yield [expression_list]
This Python keyword works much like using return , but it has some important
differences, which we'll explain throughout this article.
Generators were introduced in PEP 255 (https://www.python.org/dev/peps/pep-
0255/), together with the yield statement. They have been available since
Python version 2.2.
# generator_example_1.py
def numberGenerator(n):
number = 0
while number < n:
yield number
number += 1
myGenerator = numberGenerator(3)
print(next(myGenerator))
print(next(myGenerator))
print(next(myGenerator))
Calling the "instantiated" generator ( myGenerator ) with the next() method runs
the generator code until the rst yield statement, which returns 1 in this case.
Even after returning a value to us, the function then keeps the value of the variable
number for the next time the function is called and increases its value by one. So
the next time this function is called, it will pick up right where it left off.
Calling the function two more times, provides us with the next 2 numbers in the
sequence, as seen below:
$ python generator_example_1.py
0
1
2
while loop.
On the other hand, yield maintains the state between function calls, and resumes
from where it left off when we call the next() method again. So if yield is called
in the generator, then the next time the same generator is called we'll pick right
back up after the last yield statement.
return
When the generator nds the return statement, it proceeds as in any other
function return.
Note that return means "I'm done, and have nothing interesting to return", for
both generator functions and non-generator functions.
Let's modify our previous example by adding an if-else clause, which will
discriminate against numbers higher than 20. The code is as follows:
# generator_example_2.py
def numberGenerator(n):
if n < 20:
number = 0
while number < n:
yield number
number += 1
else:
return
print(list(numberGenerator(30)))
In this example, since our generator won't yield any values it will be an empty
array, as the number 30 is higher than 20. Thus, the return statement is working
similarly to a break statement in this case.
$ python generator_example_2.py
[]
If we would have assigned a value less than 20, the results would have been
similar to the rst example.
For example, the following code will print on the screen the values 0 to 9.
# generator_example_3.py
def numberGenerator(n):
number = 0
while number < n:
yield number
number += 1
g = numberGenerator(10)
counter = 0
The code above is similar to the previous ones, but calls each value yielded by the
generator with the function next() . In order to do this, we must rst instantiate a
generator g , which is like a variable that holds our generator state.
When the function next() is called with the generator as its argument, the
Python generator function is executed until it nds a yield statement. Then, the
yielded value is returned to the caller and the state of the generator is saved for
later use.
Note: There is, however, a syntax difference between Python 2 and 3. The code
above uses the Python 3 version. In Python 2, the next() can use the previous
syntax or the following syntax:
print(g.next())
The syntax is similar to list comprehensions, but instead of square brackets, they
use parenthesis.
For example, our code from before could be modi ed using generator expressions
as follows:
# generator_example_4.py
g = (x for x in range(10))
print(list(g))
$ python generator_example_4.py
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Generator expressions are useful when using reduction functions such as sum() ,
min() , or max() , as they reduce the code to a single line. They're also much
shorter to type than a full Python generator function. For example, the following
code will sum the rst 10 numbers:
Subscribe
# generator_example_5.py
g = (x for x in range(10))
print(sum(g))
$ python generator_example_5.py
45
Managing Exceptions
One important thing to note is that the yield keyword is not permitted in the try
part of a try/ nally construct. Thus, generators should allocate resources with
caution.
However, yield can appear in finally clauses, except clauses, or in the try
part of try/except clauses.
For example, we could have created the following code:
# generator_example_6.py
def numberGenerator(n):
try:
number = 0
while number < n:
yield number
number += 1
finally:
yield n
print(list(numberGenerator(10)))
In the code above, as a result of the finally clause, the number 10 is included in
the output, and the result is a list of numbers from 0 to 10. This normally wouldn't
happen since the conditional statement is number < n . This can be seen in the
output below:
$ python generator_example_6.py
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
The send() method resumes the generator and sends a value that will be used to
continue with the next yield . The method returns the new value yielded by the
generator.
The syntax is send() or send(value) . Without any value, the send method is
equivalent to a next() call. This method can also use None as a value. In both
cases, the result will be that the generator advances its execution to the rst
yield expression.
If the generator exits without yielding a new value (like by using return ), the
send() method raises StopIteration .
The following example illustrates the use of send() . In the rst and third lines of
our generator, we ask the program to assign the variable number the value
previously yielded. In the rst line after our generator function, we instantiate the
generator, and we generate a rst yield in the next line by calling the next
function. Thus, in the last line we send the value 5, which will be used as input by
the generator, and considered as its previous yield.
# generator_example_7.py
def numberGenerator(n):
number = yield
while number < n:
number = yield number
number += 1
Note: Because there is no yielded value when the generator is rst created, before
using send() , we must make sure that the generator yielded a value using
next() or send(None) . In the example above, we execute the next(g) line for
just this reason, otherwise we'd get an error saying "TypeError: can't send non-
None value to a just-started generator".
After running the program, it prints on the screen the value 5, which is what we
sent to it:
$ python generator_example_7.py
5
The third line of our generator from above also shows a new Python feature
introduced in the same PEP: yield expressions. This feature allows the yield
clause to be used on the right side of an assignment statement. The value of a yield
expression is None , until the program calls the method send(value) .
Connecting Generators
Since Python 3.3, a new feature allows generators to connect themselves and
delegate to a sub-generator.
The new expression is de ned in PEP 380, and its syntax is:
# generator_example_8.py
def myGenerator1(n):
for i in range(n):
yield i
print(list(myGenerator1(5)))
print(list(myGenerator2(5, 10)))
print(list(myGenerator3(0, 10)))
The code above de nes three different generators. The rst, named
myGenerator1 , has an input parameter, which is used to specify the limit in a
range. The second, named myGenerator2 , is similar to the previous one, but
contains two input parameters, which specify the two limits allowed in the range of
numbers. After this, myGenerator3 calls myGenerator1 and myGenerator2 to
yield their values.
The last three lines of code print on the screen three lists generated from each of
the three generators previously de ned. As we can see when we run the program
below, the result is that myGenerator3 uses the yields obtained from
myGenerator1 and myGenerator2 , in order to generate a list that combines the
$ python generator_example_8.py
[0, 1, 2, 3, 4]
[5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
As you can see, thanks to the yield from syntax, generators can be chained
together for more dynamic programming.
Bene ts of Generators
1. Simpli ed code
As seen in the examples shown in this article, generators simplify code in a very
elegant manner. These code simpli cation and elegance are even more evident in
generator expressions, where a single line of code replaces an entire block of code.
2. Better performance
The on-demand nature of generators also means we may not have to generate
values that won't be used, and thus would have been wasted cycles if they were
generated. This means your program can use only the values needed without
having to wait until all of them have been generated.
Wrapping Up
Generators are a type of function that generate a sequence of values. As such they
can act in a similar manner to iterators. Their use results in a more elegant code
and improved performance.
These aspects are even more evident in generator expressions, where one line of
code can summarize a sequence of statements.
Generators' working capacity has been improved with new methods, such as
send() , and enhanced statements, such as yield from .
(/author/scott/)
About Scott Robinson (/author/scott/)
Twitter (https://twitter.com/ScottWRobinson)
Subscribe
ALSO ON STACKABUSE
0 Comments StackAbuse
1 Login
LOG IN WITH
OR SIGN UP WITH DISQUS ?
Name
Ad
Follow Us
Twitter (https://twitter.com/StackAbuse)
Facebook (https://www.facebook.com/stackabuse)
RSS (https://stackabuse.com/rss/)
Newsletter
Subscribe to our newsletter! Get occassional tutorials, guides, and reviews in your
inbox.
Subscribe
Ad
Interviewing for a job?
(http://stackabu.se/daily-coding-problem)
(https://hireremote.io/remote-python-jobs)
aws (https://hireremote.io/remote-aws-
jobs) tensor ow
(https://hireremote.io/remote-tensor ow-jobs)
pytorch (https://hireremote.io/remote-
pytorch-jobs)
Consulting Engineer
Con uent a day ago
(https://hireremote.io/remote-job/1018-
consulting-engineer-at-con uent)
(https://hireremote.io/remote-job/1018-
consulting-engineer-at-con uent) java
(https://hireremote.io/remote-java-jobs)
python (https://hireremote.io/remote-
python-jobs) scala
(https://hireremote.io/remote-scala-jobs)
big data (https://hireremote.io/remote-big-
data-jobs)
security-jobs) kubernetes
(https://hireremote.io/remote-kubernetes-jobs)
shell (https://hireremote.io/remote-shell-
jobs)
Recent Posts
Tags
ai (/tag/ai/) algorithms (/tag/algorithms/) amqp (/tag/amqp/) angular (/tag/angular/)
asynchronous (/tag/asynchronous/)
Follow Us
Twitter (https://twitter.com/StackAbuse)
Facebook (https://www.facebook.com/stackabuse)
RSS (https://stackabuse.com/rss/)