How to Differentiate Iterator and Generator in Python
Iterators and generators help us generate some output or process some code iteratively, one bit at a time. In this article, we will learn some basic differences between Python’s iterators and generators with a simple example.
Iterator in Python
The basic features of an iterator are as follows:
- An iterator is an object created using a class that implements the iterator protocol. This means that the class has
__iter__
and__next__
methods defined. - The
__next__
method uses thereturn
statement to return a value. Since thereturn
statement must be the last line in that method, we must update the variable to be used in the next run of__next__
before thereturn
statement.
In the simplest case, we will look at, __iter__
returns self
.
Generator in Python
The basic features of a generator are as follows:
- A generator is a function.
- A generator function uses the
yield
keyword instead of thereturn
keyword.
2.1 Theyield
keyword yields a value and suspends the function’s execution.
2.2 The next call tonext()
resumes the execution of the code after theyield
statement.
A generator function allows us to create a generator iterator without all the extra code required when creating an iterator using a class.
Examples of Iterator and Generator in Python
The following two examples highlight the points mentioned in the previous two sections. Both examples illustrate how to generate squares of integers, starting with 1.
The first example shows how it is done using an iterator. The second example shows equivalent code using a generator.
Example Code for Iterator:
# ITERATOR (Class)
class squares(object):
def __init__(self, num1):
self.nxt_sq_of = 1
self.lim = num1
def __iter__(self):
return self
def __next__(self):
if self.nxt_sq_of <= self.lim:
ret_sq_of = self.nxt_sq_of
self.nxt_sq_of += 1
return ret_sq_of * ret_sq_of
else:
raise StopIteration
# Iterator Object
a = squares(6)
# Next value of the iterator.
next(a)
next(a)
next(a)
next(a)
next(a)
next(a)
next(a)
next(a)
# Using the iterator in a loop.
a1 = squares(6)
while True:
print(next(a1))
Output:
next(a)
Out[3]: 1
next(a)
Out[4]: 4
next(a)
Out[5]: 9
next(a)
Out[6]: 16
next(a)
Out[7]: 25
next(a)
Out[8]: 36
next(a)
Traceback (most recent call last):
File "<ipython-input-9-15841f3f11d4>", line 1, in <module>
next(a)
File "<ipython-input-1-9dbe8e565876>", line 17, in __next__
raise StopIteration
StopIteration
Example Code for Generator:
# GENERATOR FUNCTION
def gen_squares(num2):
i = 1
while i <= num2:
yield i * i
i += 1
# Generator iterator.
b = gen_squares(5)
# Next yield of the generator iterator.
next(b)
next(b)
next(b)
next(b)
next(b)
next(b)
Output:
next(b)
Out[3]: 1
next(b)
Out[4]: 4
next(b)
Out[5]: 9
next(b)
Out[6]: 16
next(b)
Out[7]: 25
next(b)
Traceback (most recent call last):
File "<ipython-input-8-adb3e17b0219>", line 1, in <module>
next(b)
StopIteration
Conclusion
We find that Python’s generators allow us to write concise code to create a generator iterator.
Iterators, on the other hand, are much more powerful because they allow the programmer to write custom code for the __iter__
method.
For more details, refer to the following.
- Python Wiki article for generators.
- Python Wiki article for iterators.