Python Iterators



Python Iterators

An iterator is an object that contains a countable number of values.

In Python, you can find iterators everywhere. They are implemented within for loops, generators, comprehensions etc.

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

Technically, in Python, an iterator object must implement two special methods, __iter__() and __next__().


Iterator vs Iterable

An object is called iterable if we can generate an iterator from it. They are many iterable objects in python like list, tuple, set, dictionary string.

To return an iterator from iterable objects, we can use the iter() function (which internaly call the __iter__() method).

In the following example, we will return an iterator from a list and print each value using the iter() andnext() method.

my_list = ['kiwi', 'apple', 'blueberry']
my_iter = iter(my_list)

print(next(my_iter))
print(next(my_iter))
print(next(my_iter))

Output

kiwi 
apple 
blueberry

Strings are also iterable objects, containing a sequence of characters. In the following example, we will iterate over a string and print each character.

my_str = 'apple'
my_it = iter(my_str)

print(next(my_it))
print(next(my_it))
print(next(my_it))
print(next(my_it))
print(next(my_it))

Output

a
p
p
l
e

Iterating Through an Iterator

To iterate through all the items of an iterator, we can use the next() function. When the end of an iterable object is reached, and there is no more data to be returned, the StopIteration exception will be raised.

# define a tuple
my_tuple = ('kiwi', 'apple', 'blueberry')

# get an iterator using iter()
my_iter = iter(my_tuple)

# iterate through a tuple using next()

# output: kiwi
print(next(my_iter))

# output: apple
print(next(my_iter))

# next(obj) is same as obj.__next__()

# output: blueberry
print(my_iter.__next__())

# this will raise an error
next(my_iter)

Output

kiwi
apple
blueberry

Traceback (most recent call last)
...
---> 21 next(my_iter)
...
StopIteration: 

The for loops uses an elegant way of automatically iterating. It can iterate over any object that can return an iterator.

my_fruits = ('kiwi', 'apple', 'blueberry')

for fruit in my_fruits:
    print(fruit)

Output

kiwi
apple
blueberry

Working of for loop for Iterators

As discussed above, the for loop can iterate automatically through the tuple.

The for loop can iterate over any iterable. Let us see how the for loop is implemented in Python.

for item in iterable:
    # do something with item

The above for loop is implemented as the following.

# create an iterator from the given iterable 
iter_obj = iter(iterable)

# infinite loop
while True:
    try:
        # get the next item
        item = next(iter_obj)
        # do something with item
    except StopIteration:
        # if StopIteration is raised break from the loop
        break

As we can see above from the implementation of the for loop, it creates an iterator object by calling the iter() method on the iterable.

The for loop is an infinite while loop that keeps calling the next() method to get the next item and executes the body of the for loop with its value. When the iterator arrives at the end of the iterable object, a StopIteration exception is raised, which is internally caught, and the loop ends.


Create an Iterator

Creating an iterator from scratch is easy in Python. We just need to implement the __iter__() and the __next__() methods.

The __iter__() method returns the iterator object itself. It can also be used to perform some initialization.

The __next__() method allows to do some operations and must return the next item in the sequence. When reaching the end of the sequence and after a subsequent call, it must raise the StopIteration exception.

In the following example, we will build an iterator that will give an even number in each iteration. The iterator will start from zero to a user set number.

class Even:
    """Class to implement an iterator to generate even numbers"""

    def __init__(self, max=0):
        self.max = max
        self.result = 0

    def __iter__(self):
        self.result = 0
        self.n = 0
        return self

    def __next__(self):
        if self.n == 0:
            self.n += 1 
            return self.result
        if self.result < self.max:
            self.result += 2
            self.n += 1
            return self.result
        else:
            raise StopIteration

# Create an object
numbers = Even(8)

# Create an iterable from the object
i = iter(numbers)

# using next to get to the next iterator element
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))

Output

0
2
4
6
8

Traceback (most recent call last)
<ipython-input-17-da0cc2cf8404> in <module>()
..
---> 36 print(next(i))
...
StopIteration: 

We can also use a for loop to iterate over our iterator class.

for n in Even(8):
    print(n)

Output

0
2
4
6
8

Python Infinite Iterators

When creating an iterator object in Python, it is unnecessary for the item in the iterator object to be exhausted. There can be infinite iterators (That never ends). We must take some precautions when handling infinite iterators.

We can build our own infinite iterators. The following iterator will return all the even numbers.

class Even:
    """Class to implement an iterator to generate even numbers"""

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        num = self.n
        self.n += 2 
        return num

# Create an object
numbers = Even()

# Create an iterable from the object
i = iter(numbers)

# using next to get to the next iterator element
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))

Output

0
2
4
6
8
10

It is recommended to include a terminating condition when working with infinite iterators.

The significant advantage of using iterators is that they save resources. As we can see above, we could get all the even numbers without storing the entire number system in memory.

There's another way more easy to build iterators in Python. To learn more about it read: Python generators using yield



ExpectoCode is optimized for learning. Tutorials and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using this site, you agree to have read and accepted our terms of use, cookie and privacy policy.
Copyright 2020-2021 by ExpectoCode. All Rights Reserved.