Signup/Sign In
LAST UPDATED: NOVEMBER 3, 2021

Iterators in Python- Part 2

    In part 1 of the iterators post, we understood what an iterator is, how it works, the methods it calls, and how it can be used with the iter method explicitly. If you haven't seen that post, it is strongly suggested to understand those concepts and then come back to this post.

    In today's post, we will see how the for loop automatically iterates through an iterable, how to build your own iterator and the concept of infinite iterators.


    1. The for loop with iterators

    The for loop looks something like this:

    for value in data_structure:
        print(value)

    It can be seen from the for loop that there is no need to explicitly create an iterator or call the next method to display the subsequent data elements. This is because for loop has been designed to automatically iterate through any iterable without the need to explicitly call the __iter__ and __next__ methods.

    Under the hood, the for loop creates an iterator object by calling the iter() method on the iterable that is used in the for loop. Inside this loop, the next method is called until all the elements inside the iterable are not covered. Once all the elements are covered, a StopIteration error is raised which is again handled internally by the for loop which results in the ending of the loop. It has been explained in code below:

    iter_obj = iter(iterable)  # An iterator object is created by passing the iterable (object) to the iter method
    
    while True:                # This is an infinite while loop that runs until the last element has been returned
        try:
            element = next(iter_obj)   # The next method to get hold of the next element in the iterable
            # do something with the value
        except StopIteration:  # if StopIteration is raised, break from loop 
            break

    In the code example above, we have used the while loop to describe the internals of a for loop in python.


    2. Building your own iterators

    As trivial as it sounds, building customized iterators isn't a tough task. Just implement the methods that an iterator calls under the hood, i.e __iter__ method, and __next__ method.

    The __iter__ method helps in returning the iterator object (if needed, changes, including initialization can be done here) after which the __next__ method is implemented, that helps in returning the next data element present in the iterable sequence. Once all the elements in the sequence are iterated over, the StopIteration error is raised, which is handled on its own.

    Below is an example that demonstrates customizing an iterator,

    class Studytonight:
        def __init__(self, max = 0):
            self.max = max
    
        def __iter__(self):
            self.n = 0
            return self
    
        def __next__(self):
            if self.n <= self.max:
                result = 12 * self.n
                self.n += 1
                return result 
            else:
                raise StopIteration
                
    class_instance = Studytonight(2)
    i = iter(class_instance)
    print(next(i))
    print(next(i))
    print(next(i))
    print(next(i))
    

    Output:

    0
    12
    24
    Traceback (most recent call last):
    
      File "<ipython-input-8-716c1e69f048>", line 23, in <module>
        print(next(i))
    
      File "<ipython-input-8-716c1e69f048>", line 15, in __next__
        raise StopIteration
    
    StopIteration

    In addition to calling the next method, our custom iterator can be used in a for loop too. See below,

    for i in Studytonight(3):
        print(i)

    Output:

    0
    12
    24
    36


    3. Infinite iterators

    There is no rule which says iterators need to be used on an iterable sequence that has finite data elements, there can be infinite iterators as well. Such iterators need to be handled very carefully in case it is used in production-level code. Below is an example, that shows how infinite iterator is used.

    The next method can be called an innumerable number of times and it never raises StopIteration error (in the below code) since the value returned by iter method will never be equal to 0. Here, we have used the int() method which returns 0 and it is being compared with the return value of iter method.

    print(int())
    
    infinite_iterator = iter(int, 5)
    next(infinite_iterator)   # call this line how many ever times you wish to, it will never give StopIteration error

    Output:

    0


    What are the advantages of using iterators?

    1. They help save resources on the system, by utilizing as less memory as possible.

    2. They make the code readable and clean.


    Conclusion

    In this post, we understood how for loop works with iterators, building one's own iterators and infinite iterators. Iterators play an important role in Python applications. So don't forget to get a hold on these concepts.

    You may also like:

    I love writing about Python and have more than 5 years of professional experience in Python development. I like sharing about various standard libraries in Python and other Python Modules.
    IF YOU LIKE IT, THEN SHARE IT
    Advertisement

    RELATED POSTS