Topics

Iterable and Iterator

In any programming language especially python, Iteration simply means to access each item of something one after another generally using a loop. For instance, a list of car names can be printed out one by one using a for loop, like this:

# list of cars
cars = ['Hyundai', 'BMW', 'Jaguar', 'Kia', 'MG']

for car in cars:
  print("Name of car: ", car)

Output:

Name of car:  Hyundai
Name of car:  BMW
Name of car:  Jaguar
Name of car:  Kia
Name of car:  MG

The process shown above was iteration which involved accessing all the items of the list one by one.

 

What is an Iterable?

An iterable in python programming is referred to as anything that can be looped through or can be iterated over.

For instance, lists, tuples, dictionaries, etc. all are iterable. In simpler words, anything that can appear on the right side of a for-loop: for x in iterable: … is iterable.

One important property of an iterable is that it has an __iter__() method or iter() method and this method specifically allows any iterable to return an iterator object.

Internally the iter() method makes a call to the __iter__() method and returns an iterator object.

 

What is an Iterator?

An iterator is an object which can be looped through and it maintains its state during the iteration by remembering where it is during an iteration i.e. It has the last memory.

An Iterator in python programming has a __next__() method that returns the next value in the iteration and updates the state to point at the next value.

Iterators also have a method that returns a self-object known as the __iter__ method.

 

Getting an Iterator object from Iterable

Iterator object can easily be gotten either by passing a predefined or pre-created iterable to the iter() method or by using the __iter__() method on that iterable.

Check out this example: 

# list of cars
cars = ['Audi', 'BMW', 'Jaguar', 'Kia', 'MG', 'Skoda']

# get iterator object using the iter() method
cars_iter = iter(cars)

# use the next method to iterate through the list
print(next(cars_iter))

# let's do it once more
print(next(cars_iter))

# want to see how to use __next__()
print(cars_iter.__next__())

# one more time
print(cars_iter.__next__())

# lets finish the list
print(cars_iter.__next__())
# this will print Skoda which is the last element
print(cars_iter.__next__())

# this will give error
print(cars_iter.__next__())

Output:

Audi
BMW
Jaguar
Kia
MG
Skoda

Traceback (most recent call last):
  File "/tmp/sessions/52ed47aa5f103wwe/main.py", line 25, in <module>
    print(cars_iter.__next__())
StopIteration

 

In the above example, the iter() method was used, well the __iter__() method can also be used like this:

# we can use __iter__() method too
cars_iter_new = cars.__iter__()

# print the data
print(cars_iter_new.__next__())

Depicting from the example above, the state of the corresponding iterator is incremented and the value that is next to the previous value is returned through the next() or the __next__() method.

Also, there is an exception in the output that won't be unnoticed and the exception is the StopIteration exception which occurs when there are no values left in an iterator to iterate i.e. after the list has ended and to call the next() or __next__() method is called.

 

Is this how for Loop works internally?

Python provides loops like the for loop which internally performs all the steps mentioned above. It minimizes the stress involved in getting the iterator object and then calling the next function again and again.

So whenever a for loop is used like the below sample:

# list of cars
cars = ['Audi', 'BMW', 'Jaguar', 'Kia', 'MG', 'Skoda']

for car in cars:
  print("Name of car: ", car)

The for loop does this internally:

# iterator object is created
iter_obj = iter(cars)

while True:
	try:
		element = next(iter_obj)
		# perform some operation like print the value
	except StopIteration:
	    # break out of loop when the list ends 
		break

In python programming, for loop is created to aid the iteration of iterable items using the iterator object as it works internally.

 

Making a class Iterable by creating custom Iterator

Creating our iterators is not a herculean task as it involves implementing __iter__() and __next__() methods.

An object of the iterator which in our case will be the object of the class itself will be returned when the __iter__() method is used.

And the next element will be returned every time the __next__() method is called thereby raising the StopIteration exception upon reaching the end.

Below is a simple class Counting which will print the counting starting from 1 till the number provided while the class is initiated.

class Counting:
    def __init__(self, limit):
        self.limit = limit
    
    # implementing the __iter__ method
    def __iter__(self):
        self.current = 0
        return self
    
    # implementing the __next__ method
    def __next__(self): 
        if self.current < self.limit:
            self.current += 1
            return self.current
        else:
            raise StopIteration
            
if __name__=='__main__':
  # create object of Counting class
  counting = Counting(10)
  
  # use for loop on it
  for x in counting:
    print(x)

Output:

1
2
3
4
5
6
7
8
9
10

Lastly, the iter() method can also be called on the object of the Counting class then employ the next() method on the iterator to print the values one by one.