In this article we will discuss the differences between Iterators and Generators in Python.
Iterators and Generators both serves similar purpose i.e. both provides the provision to iterate over a collection of elements one by one. But still both are different. Let’s discuss how they are different
Ease of implementation
Making your class Iterable and the creating Iterator class to iterate over the contents of Iterable requires some extra coding.
Let’s understand by an example,
Suppose we have a simple Range class i.e.
Frequently Asked:
class MyRange: def __init__(self, start, end): self._start = start self._end = end
Now we want to iterate over the numbers in this range using the object of our MyRange class i.e.
myrange = MyRange(10, 20) for elem in myrange: print(elem)
It will show error like this,
Traceback (most recent call last): File ".../IteratorsGenerators/gen_2.py", line 135, in <module> main() File ".../IteratorsGenerators/gen_2.py", line 121, in main for elem in myrange: TypeError: 'MyRange' object is not iterable
It returns the error because our MyRange class is not Iterable. Now we need to make our class Iterable and create an Iterator class for it i.e.
class MyRange: ''' This class is Iterable''' def __init__(self, start, end): self._start = start self._end = end def __iter__(self): return MyRangeIterator(self)
Iterator class,
Latest Python - Video Tutorial
class MyRangeIterator: ''' Iterator for class MyRange''' def __init__(self, rangeObj): self._rangeObj = rangeObj self._pos = self._rangeObj._start def __next__(self): if self._pos < self._rangeObj._end: result = self._pos self._pos += 1 return result else: raise StopIteration
Now we can iterate over the numbers in range using MyRange class object i.e.
myrange = MyRange(10, 20) for elem in myrange: print(elem)
Output
10 11 12 13 14 15 16 17 18 19
Basically we override __iter__() function in our MyRange class to make it Iterable and the overridden __next__() function in MyRangeIteration class to make it an Iterator.
We can avoid this extra class if use Generator i.e.
Creating Range class with Generator
Instead of making our range class Iterable we can add a generator function in the class that returns a Generator object. This Generator object can be used to iterate over the numbers in range,
class MySecondRange: def __init__(self, start, end): self._start = start self._end = end def forwardTraversal(self): ''' Generator Function''' _pos = self._start while _pos < self._end: result = _pos _pos += 1 yield result
Now let’s iterate over the numbers in range using MySecondRange class object i.e.
myrange = MySecondRange(10, 20) for elem in myrange.forwardTraversal(): print(elem)
Output:
10 11 12 13 14 15 16 17 18 19
So, basically Generators servers the same purpose as Iterators but in less code.
Multiple Generators but Single Iterator
There can be a single Iterator associated with a Iterable class. For example in our Iterable class MyRange, we returned the MyRangeIterator object that iterates the numbers in range from start to end. What if we want to Iterate in reverse or in some other order ?
We can not do that using Iterators because Iterable class returns a single type of Iterator object. But we can do that using Generators.
For example, let’s add two generator functions in our MySecondRange class i.e.
class MySecondRange: def __init__(self, start, end): self._start = start self._end = end def forwardTraversal(self): ''' Generator Function''' _pos = self._start while _pos < self._end: result = _pos _pos += 1 yield result def reverseTraversal(self): ''' Generator Function''' _pos = self._end - 1 while _pos >= self._start: result = _pos _pos -= 1 yield result
Now using Generator object returned by forwardTraversal(), we can iterate over the numbers in range in forward direction i.e.
myrange = MySecondRange(10, 20) for elem in myrange.forwardTraversal(): print(elem)
Output:
10 11 12 13 14 15 16 17 18 19
Whereas, using Generator object returned by reverseTraversal(), we can iterate over the numbers in range in backward direction i.e.
myrange = MySecondRange(10, 20) for elem in myrange.reverseTraversal(): print(elem)
Output:
19 18 17 16 15 14 13 12 11 10
So, unlike Iterator, with Generators we can iterate over the elements in multiple ways.
Complete example is as follows.
class MyRangeIterator: ''' Iterator for class MyRange''' def __init__(self, rangeObj): self._rangeObj = rangeObj self._pos = self._rangeObj._start def __next__(self): if self._pos < self._rangeObj._end: result = self._pos self._pos += 1 return result else: raise StopIteration class MyRange: ''' This class is Iterable''' def __init__(self, start, end): self._start = start self._end = end def __iter__(self): return MyRangeIterator(self) class MySecondRange: def __init__(self, start, end): self._start = start self._end = end def forwardTraversal(self): ''' Generator Function''' _pos = self._start while _pos < self._end: result = _pos _pos += 1 yield result def reverseTraversal(self): ''' Generator Function''' _pos = self._end - 1 while _pos >= self._start: result = _pos _pos -= 1 yield result def main(): myrange = MyRange(10, 20) for elem in myrange: print(elem) print('*** Using Generator to Iterate over a range ***') myrange = MySecondRange(10, 20) for elem in myrange.forwardTraversal(): print(elem) print('*** Using Generator to Iterate in Rerverse over a range ***') myrange = MySecondRange(10, 20) for elem in myrange.reverseTraversal(): print(elem) if __name__ == '__main__': main()
Output:
10 11 12 13 14 15 16 17 18 19 *** Using Generator to Iterate over a range *** 10 11 12 13 14 15 16 17 18 19 *** Using Generator to Iterate in Rerverse over a range *** 19 18 17 16 15 14 13 12 11 10
Latest Video Tutorials