Types of iterators in Python, Which One to Use?
Tags: programming, python, beginners, iterators
Published: 2021-11-10 10:34:53

**In this Article we will write**
  
* code for various iterators in python

* comparing the time taken by the iterator to iterate

* when to use which iterator

*if you're new to this topic, i would prefer you go with this article linked below first:-**

---> [What is iterator,iterable in Python](https://dev.to/adarshrawat7400/what-is-iterableiterator-in-python-3325)

### __Types of Iterators in Python__

1. Forward Iterator (for iterator)
2. Reverse Iterator
3. Index Based Iterator
4. Enumerate Iterator
5. Parallel Iterator
6. File Iterator


* **Forward Iterator**

1. It is a read-only iterator, means you can only travese over it items in iterable, cannot modify values
2. It is the fastest way to iterate/traverse over the iterable (*Collection on values*)
3. As it's name suggest it can move only forward, going at the previous element is not possible.

### Example :-
```
>>> names = ['ram','shyam','ajay','bipin','manoj','alex']
>>> for name in names:
...     name = name.upper()
...     print(name)
...
RAM
SHYAM
AJAY
BIPIN
MANOJ
ALEX
>>> names
['ram', 'shyam', 'ajay', 'bipin', 'manoj', 'alex']
```

As we can see in the above example *for loop* uses forward iterator,to iterable over the name in the *names* list,
**we also tried to modify each value, by explicitly assigining 
it to new uppercase value but, it does not modifiy the name in the names list, as we can see on the last line**.

* **Reverse Iterator**

1. It is a read-only iterator, means you can only travese over it items in iterable, cannot modify values
2. Iterate over element in the reverse order ,starting from last element to first element in the iterable.
3. it has same efficiency as forward itertor
4. *reversed()* function is used to return *list_reverseiterator object*

### Example :-
```
>>> names
['ram', 'shyam', 'ajay', 'bipin', 'manoj', 'alex']
>>> reverse_iter = reversed(names)
>>> reverse_iter
<list_reverseiterator object at 0x000001D28CA21D20>
>>> for name in reverse_iter:
...     print(name)
...
alex
manoj
bipin
ajay
shyam
ram
>>>
```

The reversed object return *list_reverseiterator* object which then traveres from last element to first element.

* **Index Based Iterator**

1. It uses a special function -
   * range(start,stop,stepby),
   * range(start,stop),
   * range(stop)

2. Suitable when we have to perfrom read/write operation on the iterable object.

3. Suitable for *INPLACE* list operations

4. It is slower compared to for iterator

### Example :-
```
>>> names = ['ram','shyam','ajay','bipin','manoj','alex']
>>> for i in range(len(names)):
...     names[i] = names[i].upper()
...     print(names[i])
...
RAM
SHYAM
AJAY
BIPIN
MANOJ
ALEX
>>> names
['RAM', 'SHYAM', 'AJAY', 'BIPIN', 'MANOJ', 'ALEX']
```

As you can see we used *range()* function to traverse/iterate over the list using *index*, and also able to modify the value *INPLACE*.


* **Enumerate Iterator**

1. It uses a special function -
   * *enumerate(iterable)*, 
      return *enumerate* object

2. Suitable for *Mutable* iterable objects

3. Can be used for *INPLACE* Operations

4. Slow as compared to forward Iterator

### Example :-
```
>>> names = ['ram','shyam','ajay','bipin','manoj','alex']
>>> names_iter = enumerate(names)
>>> names_iter
<enumerate object at 0x000001D28CA413C0>
>>> for index,value in names_iter:
...     names[index] = value.upper()
...
>>> names
['RAM', 'SHYAM', 'AJAY', 'BIPIN', 'MANOJ', 'ALEX']
```

*enumerate()* function return *enumerate object* which iterate over a list of *tuples* where

1. first element of each tuple is it's corresponding index 
2. second element is the value at store at the index
3. with the help of index we can modify the value in the *names* list


* **Parallel Iterator**

1. It uses a special function -
   * *zip(iter1, iter2, iter3, iter4....)*

2. Suitable when we have to *iterate* over mutile *iterable* object at the same time.

3. Return *zip* object 

### Example :-
```
>>> first_names = ['ram','shyam','ajay','bipin','manoj','alex']
>>> last_names = ['gupta','tiwari','yadav','rawat','desai','khan','raven']
>>> home_town = ['ayodhya','vrindavan','bihar','jhnasi','boston','delhi','lanka']
>>>
>>> detail_iter = zip(first_names, last_names,home_town)
>>> detail_iter
<zip object at 0x000001D28CA41B00>
>>> for detail in detail_iter:
...     print(detail)
...
('ram', 'gupta', 'ayodhya')
('shyam', 'tiwari', 'vrindavan')
('ajay', 'yadav', 'bihar')
('bipin', 'rawat', 'jhnasi')
('manoj', 'desai', 'boston')
('alex', 'khan', 'delhi')
```

*zip()* function return *zip object* which iterate over a list of *tuples*  where tuples size is equal to the no. of list passed in the *zip()* function

* also, it will return list of tuples of size equal to the length of (minimum size *iterable* among all the *iterables* passed in the *zip()* function)
i.e 
    * *first_names* list has *length* of 6
    * *last_names* list has *length* of 6
    * *home_town* list has *length* of 7

So, it return list of tuples with list size 6


* **File Iterator**

1. It uses a special function -
     * *open(filename)*
     
2. It is used to iterate over the contents of the file

### Example :-
```
File Content :- 
Details :-
('ram', 'gupta', 'ayodhya')
('shyam', 'tiwari', 'vrindavan')
('ajay', 'yadav', 'bihar')
('bipin', 'rawat', 'jhnasi')
('manoj', 'desai', 'boston')
('alex', 'khan', 'delhi')

Code :-

>>> for line in open("hello.txt"):
...     print(line)
...
Output :-

Details :-

('ram', 'gupta', 'ayodhya')

('shyam', 'tiwari', 'vrindavan')

('ajay', 'yadav', 'bihar')

('bipin', 'rawat', 'jhnasi')

('manoj', 'desai', 'boston')

('alex', 'khan', 'delhi')

```
It reads only line at a time

Now, that we have covered all the 6 *iterators*, we now see when to use which iterator

### __When to Use Which Iterator__?
when we need to :- 
* traverse on a TUPLE (readonly) - **forward iterator**

* traverse on a LIST (readonly) - **forward iterator**

* traverse modify list **inplace** - **index based iterator**

* traverse mutiple **iterable** (readonly)   - **parallel iterator**

### __Comparing Performance of Iterators__
Now, we will going to compare performance of 
* (*forward iterator, reverse iterator, index based iterator,  enumerate iterator*)
* by iterating a list of size 10000
* comparing the time taken by various iterator 

### Example :-

```
Code :- 
import time
lst = [i for i in range(10000000)]
print("Traversing over list of size 10000000 \n-----------------------------------------------------\n")

start_time = time.time()
for i in lst:
     print("",end="")
end_time = time.time()
print(f"Time take by forward iterator {round(end_time - start_time ,2)} Seconds")

##################################################################

start_time = time.time()
for i in reversed(lst):
     print("",end="")
end_time = time.time()
print(f"Time take by reverse iterator {round(end_time - start_time ,2)} Seconds")


##################################################################

start_time = time.time()
index_based_iter = range(len(lst))
for i in index_based_iter:
     print("",end="")
end_time = time.time()
print(f"Time take by index based iterator {round(end_time - start_time ,2)} Seconds")


##################################################################

start_time = time.time()
index_based_iter = range(len(lst))
for i in index_based_iter:
     print("",end="")
end_time = time.time()
print(f"Time take by enumerate iterator {round(end_time - start_time ,2)} Seconds")


Output :-
Traverseing over list of size 10000000
-----------------------------------------------------

Time take by forward iterator 6.99 Seconds
Time take by reverse iterator 7.45 Seconds
Time take by index based iterator 8.32 Seconds
Time take by enumerate iterator 7.15 Seconds
```

Now, we have some to end, hope you all are now fimilar with various types of iterators in python and when to use which iterator.
See you next time , covering some other topic :-)