Iterators

  • Compared to many other programming languages, Python’s syntax is very clear and simple. For intance, see for..in loop below:

numbers = [1, 2, 3, 4, 5]
for number in numbers:
    print(number, end=" ")
1 2 3 4 5 
  • But how does the above loop in the code fetches the individual elements from the given list and loop for.

  • How to use this construct in our own objects if we have to?

Iterator Protocol

  • To create our oun iterator object, we’ll need to implement two dunder(double underscore) methods in the class:

    • __iter__() (iter()): should return iterator object. Used in for and in keywords

    • __next__() (next()): returns next value or StopIteration error once all the objects has been looped. Moreover, this method internally uses iter() method to get an iterator object and the uses next() method to iterate over the values.

  • Let’s have a look at the code below:

numbers = [1, 2, 3]
iterable_numbers = iter(numbers)

print("Calling 1st time: ", next(iterable_numbers))

print("Calling 2nd time: ", next(iterable_numbers))

print("Calling 3rd time: ", next(iterable_numbers))

print("Calling 4th time: ", next(iterable_numbers)) # Raises StopIteration
Calling 1st time:  1
Calling 2nd time:  2
Calling 3rd time:  3
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-2-cdd05496ca3a> in <module>
      8 print("Calling 3rd time: ", next(iterable_numbers))
      9 
---> 10 print("Calling 4th time: ", next(iterable_numbers)) # Raises StopIteration

StopIteration: 
  • Now, lets create our own custom iterator below:

class Increment:
    def __init__(self, start, end, step = 1):
        self.current = start
        self.end = end
        self.step = step
    
    def __iter__(self):
        """returns the iterator object"""
        return self
    
    def __next__(self):
        """returns the next value or raises StopIteration"""
        if self.current > self.end:
            raise StopIteration("Stop Iteration")
        else:
            self.current += self.step
            return self.current - self.step
  • Lets take our sweet custom iterator Increment into action:

for i in Increment(0, 10):
    print(i, end=", ")

print("")
for i in Increment(0, 20, 2):
    print(i, end=", ")
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 
  • An iterator object can only be used one. After the StopIteration exception has been raised, it will continue raising the same exception.

  • Lets look at the code below for instance,

increment = Increment(0, 3)

print(next(increment))
0
print(next(increment))
1
print(next(increment))
2
print(next(increment))
3
print(next(increment))
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-74-37e11a149c41> in <module>
----> 1 print(next(increment))

<ipython-input-64-be979cba6339> in __next__(self)
     10     def __next__(self):
     11         if self.current > self.end:
---> 12             raise StopIteration("Stop Iteration")
     13         else:
     14             self.current += 1

StopIteration: Stop Iteration
print(next(increment)) # Raises StopIteration again
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-80-06d940e8f2eb> in <module>
----> 1 print(next(increment)) # Raises StopIteration again

<ipython-input-64-be979cba6339> in __next__(self)
     10     def __next__(self):
     11         if self.current > self.end:
---> 12             raise StopIteration("Stop Iteration")
     13         else:
     14             self.current += 1

StopIteration: Stop Iteration
  • Using while loop for our Increment instead of for…in

increment = Increment(0, 3)

while True:
    try:
        i = increment.__next__() # or next(increment)
        print(i, end=", ")
    except StopIteration as error:
        print(error, "Raised")
        break
0, 1, 2, 3, Stop Iteration Raised