Iterators and Generators in Python

Iterators :-

An iterator is an object that can be iterated upon, meaning that you can traverse through all the values.

Technically, in Python, an iterator is an object which implements the iterator protocol, which consist of the methods __iter__() and __next__().

To create a Python iterator object, you will need to implement two methods in your iterator class.

  • __iter__: This returns the iterator object itself and is used while using the “for” and “in” keywords.
  • __next__: This returns the next value. This would return the StopIteration error once all the objects have been looped through.

Let us create an iterator

>>> class ListIterator:
        def __init__(self, list): 
            self.__list = list 
            self.__index = -1 
        def __iter__(self): 
            return self 
        def __next__(self):
            self.__index += 1
            if self.__index == len(self.__list):
                raise StopIteration  
            return self.__list[self.__index]   

Now you can call the above class as an iterator. Which means you can run the next function on it.

>>> a = [1, 2, 3, 6, 5, 4]
>>> mylist = ListIterator(a)
>>> for _ in range(6):
       print(next(mylist)) 

Generators:-

Generators are a simple way of creating iterators .This is done by defining a function but instead of the return statement returning from the function, use the “yield” keyword. For example, see how you can get a simple vowel generator below.

>>> def my_generators_func1():
       yield 'a'
       yield 'e'
       yield 'i'
       yield 'o'
       yield 'u'
  
>>> x = my_generators_func1()
  
>>> print(next(x))
>>> print(next(x))
>>> print(next(x))
>>> print(next(x)) 
>>> print(next(x)) 
 

Generators VS Iterators:-

Let’s summarize the whole idea of Generator and Iterators..

  •  Generators are similar to iterators but they’re more simple to use and create .
  • Generators are easy to implement and they’re memory efficient.
  • Biggest advantage of generator is that we don’t need to implement over “next” method and we also don’t have to raise stopIteration error from our function/class. So you don’t need to throw any exception. Generator will take care of throwing this exception(StopIteration)  for you by itself.  
  • In case of Generators , yield statement pauses the function and saves the state of that function. On the other hand , in case of Iterators return statement is immediately terminated entirely. After return ,you can not do anything inside the function.
  • Whenever you need to create iterators then they’re more lengthy and sometimes counter intuitive.
  • In case of generator, the method like iter and next are automatically implemented which make bit concise.

Let’ check for StopIteration exception error using an example below-

>>> def my_generators_func2():
       for i in range(5):
           print('--------------------', i)
           yield i
  
>>> x = my_generators_func2()
  
>>> print(next(x))
>>> print(next(x))
>>> print(next(x))
>>> print(next(x)) 
>>> print(next(x)) 
>>> print(next(x)) 
 

As we can see everything worked fine until 6th print statement since list exhausted and our generator function has raised this exception by itself.

0

Leave a Reply