Python Iterators

Python Iterators
An iterator is simply an object that contains the countable number of values.

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

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

Iterator vs Iterable
Lists, tuples, dictionaries, and sets are all iterable objects. They are iterable containers which you can get an iterator from i.e. they contain the iterable objects inside them.

All these objects have a iter() method which is used to get an iterator:
Return an iterator from a tuple, and print each value:

mytuple = ("apple", "banana", "cherry")

myit = iter(mytuple)

print(next(myit))

print(next(myit))

print(next(myit))


Even and every strings is an iterable object, and can return an iterator:

mystr = "banana"

myit = iter(mystr)

print(next(myit))

print(next(myit))

print(next(myit))

print(next(myit))

print(next(myit))

print(next(myit))


Looping Through an Iterator
An iterable object can be iterated with the help of for loop:
For e.g if we want to iterate the values of a tuple, then we have to write the folowing code:

mytuple = ("apple", "banana", "cherry")

for x in mytuple:

     print(x)


Example: Iterate the characters of a string:

mystr = "banana"

for x in mystr:

     print(x)


The "for" loop actually creates an iterator object and then executes the next() method for each loop.

Create an Iterator
If you want to create an object or class as an iterator then you have to implement the functions __iter__() and __next__() to your object.

As you have learned in the Python Classes/Objects module that all classes have a function called __init__(), thay allows you do some initialization when the object of the class is being created.

The __iter__() function works in similar manner, you can do operations like initializing etc., but must always return the iterator object itself.

The __next__() function also allows you to do operations, and must return the next item in the sequence.
For better understanding this consider an example to create an iterator that returns numbers, starting with 1, and each sequence will increase by one i.e. it should return sequence like 1,2,3,4,5 etc.:

class MyNumbers:

     def __iter__(self):

     self.a = 1

     return self

def __next__(self):

     x = self.a

     self.a += 1

     return x

myclass = MyNumbers()

myiter = iter(myclass)

print(next(myiter))

print(next(myiter))

print(next(myiter))

print(next(myiter))

print(next(myiter))


Stop Iteration
The above example will run forever if it had enough next() statements or a for loop is used in this.

To prevent this iteration to go on forever, the StopIteration statement is used to stop the iteration.

In the __next__() function, we can add a terminating condition that will raise an error if the iteration is done for a specified number of times:
For e.g. if we want to stop after 20 iterations, then we write the following code for that:

class MyNumbers:

     def __iter__(self):

     self.a = 1

     return self

def __next__(self):

     if self.a <= 20:

         x = self.a

         self.a += 1

         return x

     else:

         raise StopIteration

myclass = MyNumbers()

myiter = iter(myclass)

for x in myiter:

     print(x)