Python Advanced Topics

Python Iterators

Iteration is one of the most significant concepts in Python. Iterators are Python’s ubiquitous spirits. They’re all over the place, and you’ve probably seen them in one of your programs. Iterators in Python are objects that allow you to iterate through all the elements in a collection, regardless of their implementation. This article will go over Python iterator and iterables along with how you can build your own iterator using __iter__ and __next__ methods. We will go over the definitions of each of these items and work toward acquiring a grasp of the underlying principles behind each object and the Python iterator concept.

Iteration in Python

Iteration is defined in programming as the repetition of a block of code a given number of times. In layman’s terms, iteration means repeating steps. We can utilize loops like for loop, etc. to achieve iterations.

Iteration is not a standalone concept; it is associated with two other terms: iterators and iterables.

Iterator & Iterable

  • Iterable – Iterable is a type of object that can be iterated over. When provided to the iter() method, it generates an Iterator. Iterable objects include lists, tuples, dictionaries, strings, and sets. They are iterable containers that can be turned into iterators.
  • Iterator – An iterator is an object that implements the iterator protocol, which is a Python class with the __next()__ That is, an iterator understands how to compute the next value whenever you ask for it. The function iter() can be used to build an iterator from an iterable. It is worth noting that every iterator is also an iterable.

Iterators in Python

Iterators are objects that can be iterated on, which means that the user can go over their values one by one. In Python, an iterator is an object used to iterate across iterable objects such as lists, tuples, dicts, and sets. The iter() method is used to initialize the iterator object. Iteration is accomplished through the usage of the next() method.

Iterators can be found all across Python. They are ingeniously implemented within for loops, comprehensions, generators, and so on, but are concealed from view. Iterators aid in the production of cleaner code by allowing us to work with endless sequences without having to reallocate resources for every potential sequence, hence saving resource space.

Python iterator implements the iterator protocol, which includes two special methods: __iter__() and __next__().

  • The __iter__() method – returns an iterator object. This is required in order to use containers and iterators with the for and in statements.
  • The __next__() method – This function returns the sequence’s next item. If no more items are found, throw the StopIteration exception.

Example

                    

class Squares:
    def __init__(self, length):
        self.length = length
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.length:
            raise StopIteration

        self.current += 1
        return self.current ** 2

square_num = Squares(5)

for sq in square_num:
    print(sq)

print(next(square_num))

Output

                    

1
4
9
16
25
Traceback (most recent call last):
File "", line 21, in 
File "", line 11, in __next__
StopIteration

Explanation

In the __init__ method, we first establish the length and current attributes, where the length attribute defines the number of square numbers that the class should return. And the current attribute keeps track of the integer that is currently in use. The __iter__ method, which returns the self object, is then implemented. The __next__ method, which returns the next square number, is implemented in the third phase. If the number of square numbers returned exceeds the length, the __next__ method throws the StopIteration exception.

Iterating Through an Iterator

To manually iterate through all the items of an iterator, we can use the next() function or the __next__() method. When we reach the end of the loop and there is no more data to return, the StopIteration Exception is thrown.

Example

                    

# define a list
my_list = [1, 2, 3, 'Numbers']

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

# iterate through it using next()
print(next(my_iter))   # Output: 1
print(next(my_iter))   # Output: 2

# This is same as next(obj)
print(my_iter.__next__())   # Output: 3
print(my_iter.__next__())   # Output: Numbers

# This will raise error since no items left in the list to be iterated
next(my_iter)

Output

                    

1
2
3
Numbers
Traceback (most recent call last):
File "", line 16, in 
StopIteration

Working of for loop for Iterators

The above program can print the iterator values by using a for loop as well. The for loop is a more elegant approach to automatically iterating. We may use this to iterate over any object that can yield an iterator, such as a list, string, or file. For example,

                    

for element in my_list:
    print(element)

Output

But how exactly does a for loop function? It is actually implemented as,

                    

# create an iterator object from that iterable
iter_obj = iter(iterable)

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

As a result, the for loop internally produces an iterator object, iter obj, by executing iter() on the iterable. This for loop, ironically, is an infinite while loop. Inside the loop, it calls next() to acquire the next element and uses this value to perform the body of the for loop. When all of the elements have been exhausted, StopIteration is raised, which is internally caught, and the loop terminates. It should be noted that any other type of exception will be ignored.

Building Custom Iterators

Python allows users to build their own Python iterator from the ground up. All that remains is to implement the __iter__() and __next__() methods. The iterator object is returned by the __iter__() method. Some initialization can be done if necessary. The next item in the series must be returned using the __next__() method. It must raise StopIteration when it reaches the conclusion of the loop and in future calls.

Here is an example of a custom iterator.

                    

class Run:
  def __init__(self, highest):
    self.highest = highest

  # Creating an iterator object
  def __iter__(self):
      self.value = 1
      return self

  # Move to next element using __next__
  def __next__(self):
      # Storing the current i value
      value = self.value

    # Stop iteration if limit is reached
      if value > self.highest:
          raise StopIteration

    # double & return old value
      self.value = value * 2
      return value

# Outputs the numbers till value reaches highest = 50
for x in Run(50):
    print(x)

Output

Python Infinite Iterators

There may be times when we want our iterator to stop unless a certain condition is met. In such cases, we employ endless iterators. Because we don’t set a limit on it and it doesn’t raise an exception, an infinite iterator never comes to a halt. These iterators must be intentionally stopped.

Note – If we fall into the trap of endless iterators, the process would never end. As a result, greater caution must be exercised while dealing with such iterators.

If we call the built-in function iter() with two arguments, the first must be a callable object (function), and the second must be the sentinel (A value that indicates the series end).

                    

int()
print('Output of int():', int())   # Output 0
 
infinite_trap = iter(int,1)

print(next(infinite_trap))
print(next(infinite_trap))
print(next(infinite_trap))
print(next(infinite_trap))

Output

                    

Output of int(): 0
0
0
0
0

We can observe that the int() function always returns a value of zero. As a result, giving it as iter(int,1) returns an iterator that calls int() until the returned value equals 1. This never occurs, and we end up with an infinite iterator.

In addition, we can create our own endless iterators. The iterator that follows will theoretically return all odd numbers.

                    

class InfiniteIter:
    #Infinite iterator to return all even numbers
    def __iter__(self):
        self.num = 0
        return self

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

a = iter(InfiniteIter())
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))

Output

When iterating over certain types of infinite iterators, make sure to include a terminating condition. Python Iterator has the advantage of conserving resources. We could get all the odd numbers, as illustrated above, without having to memorize the full number system. In theory, we can have infinite items in the finite memory.

Frequently Asked Questions

Q1. What is an iterator in Python?

Iterators are objects that can be iterated on, which means that the user can go over their values one by one. In Python, an iterator is an object used to iterate across iterable objects such as lists, tuples, dicts, and sets. The iter() method is used to initialize the iterator object. Iteration is accomplished through the usage of the next() method.

Python iterator implements the iterator protocol, which includes two special methods: __iter__() and __next__().

Q2. Why should we use Python Iterator?

We have a lot of data in our hands nowadays, and dealing with this massive amount of data poses issues for everyone who wants to do some sort of analysis with that data. So, if you’ve ever struggled with managing large amounts of data and your machine running out of memory, you’ll appreciate Python’s Iterators notion.

As a result, rather than placing all of the data into memory at once, it would be preferable if we could work with it in bits or small chunks, dealing with only the data that is needed at the time, right? As a result, the strain on our computer RAM would be much reduced. And this is precisely what iterators do! As a result, because Iterators do not compute their items when they are formed, but only when they are called upon, you can utilize Iterators to save a lot of memory.

Q3. What are iterator and iterable in Python?

  • Iterable – Iterable is a type of object that can be iterated over. When provided to the iter() method, it generates an Iterator. Iterable objects include lists, tuples, dictionaries, strings, and sets. They are iterable containers that can be turned into iterators.
  • Iterator – An iterator is an object that implements the iterator protocol, which is a Python class with the __next()__ That is, an iterator understands how to compute the next value whenever you ask for it. The function iter() can be used to build an iterator from an iterable. It is worth noting that every iterator is also an iterable.
Share with friends

Customize your course in 30 seconds

Which class are you in?
5th
6th
7th
8th
9th
10th
11th
12th
Get ready for all-new Live Classes!
Now learn Live with India's best teachers. Join courses with the best schedule and enjoy fun and interactive classes.
tutor
tutor
Ashhar Firdausi
IIT Roorkee
Biology
tutor
tutor
Dr. Nazma Shaik
VTU
Chemistry
tutor
tutor
Gaurav Tiwari
APJAKTU
Physics
Get Started

Leave a Reply

Your email address will not be published. Required fields are marked *

Download the App

Watch lectures, practise questions and take tests on the go.

Customize your course in 30 seconds

No thanks.