Iteration refers to the process of repeating the same logic more than one time.

An iterator is an object containing a countable number of values and is used to iterate over iterable objects (list, tuples, dictionaries, sets, etc.). It implements the iterator protocol, which consists of the methods __ iter__() and __ next__().

iter() keyword is used to create an iterator containing an iterable object. next() keyword is used to call the next element in the iterable object.

Once the iterable object is completed, in order to use them again we reassign them to the same object. Example:

my_list = [1,2,3,4]
iter_obj=iter(my_list)
# Creates an interator

print(type(iterable))
# prints list_iterator 
# indicating this is an iterator
print(next(iter_obj))
# prints the first element i.e 1
print(next(iter_obj))
# prints the next element i.e 2
print(next(iter_obj))
# prints the next element i.e 3 and so on.

Here my_list is iterable and iter_obj is an iterator.

Now, let’s see how to create an iterator.

class Multiples:
  def __iter__(self):
    self.a = 1
    return self 
  def __next__(self):
      if self.a <= 10:
          x = self.a
          self.a += 1
          return x
      else:
          raise StopIteration
# Creating an object of class Multiples        
my_obj = Multiples()    
# Creating an iterator 
my_iter = iter(my_obj) 
# Iterating through an iterator   
for x in my_iter:        
  print(x)

Output

1
2
3
4
5
6
7
8
9
10

Iterator follows lazy evaluation where the evaluation of expression will be on hold and stored in the memory until the item is called which helps to avoid repeated evaluation. This is why it requires only one memory location to process the value.

Now coming to generators. A python generator function lends us a sequence of values to python to iterate on. They are implemented using a function. Here, the yield function returns the data without affecting or exiting the function. They are efficient in writing fast and compact code, this is an advantage over Python iterators. They also follow lazy evaluation. Unlike iterator which do not uses local variables, the generator uses yield keyword to save the values of the local variable. A generator may have any number of ‘yield’ statements.

def Even(max):    
    number = 1    
    while number < max:         
        number += 1           
        if (number%2 == 0):  
            yield number         

# Iterating using generator function           
for i in Even(10):           
    print(i)                  

The output is:-

2
4
6
8
10

Generators and iterators both provides considerable performance boost when used over large size datasets owing to their ability to not generate and store the data all at once, rather they do it sequentially and on demand.