Iterators in Python are a powerful feature that allows us to traverse through all elements of a collection (like lists, tuples, or dictionaries) without exposing the collection’s underlying structure. Understanding iterators is essential for writing efficient, memory-friendly Python programs.
Iterators in Python
In Python, iterators are objects that enables us to traverse through elements of a collection (like lists, tuples, or dictionaries) one at a time. Iterator concept is common to Python’s for-loops and functions like map()
and filter()
.
An iterator allows us to access elements of a collection without needing to know its underlying structure or maintain an index.
Iterable and Iterator
Iterable – An iterable is an object that contains a collection of values that we can loop over (like lists, strings, and tuples). We can convert an iterable into an iterator using Python’s built-in iter()
function. Examples of iterable are lists, strings, tuples, dictionary etc.
Iterator – An iterator is an object that represents a stream of data. It is initialized using the iter()
function and allows us to fetch one element at a time from the iterable using the next()
function. Once all elements are consumed, calling next()
raises a StopIteration
exception.
- An iterable can be converted to an iterator using
iter()
. - An iterator can fetch the next item using
next()
and raisesStopIteration
when done.
# Example of iterable and iterator
# This list is an iterable
courses_list = ["Generative AI", "Python", "Prompt Engineering", "Power BI"]
# using iter we created an iterator over the list
iterator = iter(courses_list)
# access elements from iterable - Uusing iterator next() function
print(next(iterator)) # Output => Generative AI
print(next(iterator)) # Output => Python
print(next(iterator)) # Output => Prompt Engineering
# next(iter_obj) is same as iter_obj.__next__()
print(iterator.__next__()) # Output => Power BI
# Raises StopIteration
- In this example,
courses_list
is an iterable. - Using
iter(courses_list)
we have created an iterator over thecourses_list
. - Using
next(iterator)
ORiterator.__next__()
, we can access the next element of the iterable. - When
iterator
reached end ofcourses_list
, then it will raiseStopIteration
error.
for
loop
Iterator with courses_list = ["Generative AI", "Python", "Prompt Engineering", "Power BI"]
course_iter = iter(courses_list) # create iterator over the list
# infinite while loop
while True:
try:
element = next(course_iter) # access next element from iterator
print(element)
except StopIteration: # catch StopIteration error raise by iterator
break # break the loop
Create Custom Iterator
In Python, an object is considered an iterator if it implements the iterator protocol, which consists of two methods:
__iter__()
– Returns the iterator object itself.__next__()
– Returns the next item from the collection (iterable). If no more items are left, it raises theStopIteration
exception.
We can create our own iterators (custom iterators) by defining a class that implements both __iter__()
and __next__()
methods.
class PowerElement:
"""Class to implement an iterator to calculate power of all numbers
from 1 to given number"""
def __init__(self, number, power): # class __init__ function
self.number = number
self.power = power
def __iter__(self): # __iter__ function to define iterator
self.counter = 1
return self
def __next__(self): # __next__ function to get next element from iterator
if self.counter <= self.number:
result = self.counter ** self.power
self.counter += 1
return result
else:
raise StopIteration
# create a class object
power_element_object = PowerElement(4, 3)
# create an iterable from iterator class object
i = iter(power_element_object)
# Using next to get to the next iterator element
print(next(i))
print(next(i))
print(next(i))
print(next(i))
# print(next(i)) # raise StopIteration error
- In this example, we have created
PowerElement
class, which defines__init__
,__iter__
and__next__
function __init__(self, number, power)
is a constructor to the class, which sets initial value to class attributes__iter__(self)
make the class an iterator and return its own object.__next__(self)
provide the logic to calculate the next value from iterator. In this case we are calculating the power of all the numbers from 1 to given number.- At end
__next__
function raiseStopIteration
error.
Built-in Functions Returning Iterators
Python has many built-in functions that return iterators:
range(start, end)
– This generates numbers fromstart
toend - 1
.map(function, iterable)
– Applies a function to each item of an iterable.filter(function, iterable)
– Filters elements based on a function.zip(iterable1, iterable2, ...)
– Pairs elements from multiple iterables.
# Using range (returns an iterator)
for i in range(4):
print(i) # Output => 0 1 2 3
# Using map (returns an iterator)
number_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
square_numbers = map(lambda x: x ** 2, number_list)
print(list(square_numbers)) # Output => [1, 4, 9, 16, 25, 36, 49, 64, 81]
# Use filter to get words longer than 3 characters
numbers_divisble_by_3 = filter(lambda number: number % 3 == 0, number_list)
# Convert the result to a list and print it
print(list(numbers_divisble_by_3)) # Output => [3, 6, 9]
cube_of_numbers = map(lambda x: x ** 3, number_list)
# Use zip to combine names and scores
zip_cube_of_numbers = zip(number_list, list(cube_of_numbers))
# Convert the result to a list and print it
print(list(zip_cube_of_numbers))
# Output => [(1, 1), (2, 8), (3, 27), (4, 64), (5, 125), (6, 216), (7, 343), (8, 512), (9, 729)]
Key Properties of Iterator
- Lazy Evaluation – Iterators do not compute or store all the values upfront; instead, they generate each value only when needed, one at a time. This makes them memory efficient and ideal for working with large datasets or infinite sequences.
next(iterator)
– only this element is computed now - Single Traversal (One-time Use) – Iterators are exhausted after one complete traversal. Once we iterate over all the elements, the iterator cannot be reused. After reaching the end, any subsequent call to
next()
will raise aStopIteration
exception. - Stateful – Iterators maintain an internal state to keep track of their current position in the iterable. Each call to
next()
moves the iterator forward. This internal state is why we cannot reset an iterator without creating a new one. - Implements the Iterator Protocol – An object is considered an iterator if it implements the iterator protocol, which consists of two methods =>
__iter__()
and__next__()
- Memory Efficient – Iterators are more memory-efficient than lists or other collections because they don’t store all elements in memory. Instead, they generate each element on the fly when needed. This is particularly useful when working with large datasets or streams of data.
- No Random Access – Iterators do not support random access (i.e., we cannot access an arbitrary element like we can with lists using an index). The only way to access the next element is by using
next()
. Once an element is retrieved, it cannot be revisited unless we recreate the iterator. - Use in Loops – Iterators work seamlessly in
for
loops, and Python’sfor
loop is designed to work with iterators. The loop automatically callsnext()
on the iterator and handles theStopIteration
exception behind the scenes. - Can be Infinite – Since iterators generate items one at a time, they can represent infinite sequences. We can have an iterator that never stops yielding values, which is useful in scenarios where we don’t know how many items we need upfront.
Use Cases and Benefits of Iterators
- Memory Efficiency – Because iterators are lazy, they are memory efficient. Iterators are particularly useful when dealing with large datasets or infinite sequences.
- Stream Processing – Iterators make it easy to process streams of data, like reading from a file or fetching database records. We can handle them piece by piece without loading everything into memory at once.
- Pipelining – Since iterators return one element at a time, they can be chained together to create a pipeline of transformations.
Common Mistakes to avoid
- Reusing an Iterator – An iterator can only be traversed once. After it raises
StopIteration
, it becomes exhausted. If we want to iterate over the same data again, then we need to create a new iterator. - Modifying the Iterable During Iteration – If we modify the underlying iterable (like a list) while iterating over it, we may encounter unexpected behavior or errors. It’s generally best to avoid modifying a collection during iteration.
Summary
Iterators in Python provide a powerful way to process data sequentially, enabling efficient memory usage and lazy evaluation. With built-in support for iterators in loops and functions, as well as the ability to create custom iterators and generators, Python offers a highly flexible and efficient iteration model.
In this article we learned about Iterators in Python. Following topics were covered:
Code – Github Repository
All code snippets and programs for this article and for Python tutorial, can be accessed from Github repository – Comments and Docstring in Python.