Back to Python
Lesson 21 of 27

What Is the Iterator Protocol in Python? Iterable vs Iterator.

The iterator protocol is the foundation of iteration in Python. It defines how objects can be looped over using constructs like for loops, list comprehensions, and built-in functions such as sum() and any(). Understanding this protocol explains why Python can iterate over lists, strings, files, generators, and even custom objects in a consistent and predictable way. This in-depth guide explains the iterator protocol in Python from the ground up, focusing on how iteration actually works behind the scenes. It clearly breaks down the difference between iterables and iterators, a concept that often confuses developers, and shows how Python internally uses the __iter__() and __next__() methods to control iteration. The content goes beyond definitions and covers real-world use cases such as processing large files, streaming data, and building memory-efficient pipelines. Short but meaningful code examples, comparison tables, common mistakes, and edge cases are included to help developers understand not just how iteration works, but why Python designed it this way. This guide is ideal for learning, interview preparation, and writing efficient, Pythonic code.

Iterator Protocol in Python: How Iteration Really Works Behind the Scenes

Iteration is one of the most fundamental operations in Python. From for loops and list comprehensions to functions like sum(), any(), and map(), iteration is everywhere. What makes Python powerful is that iteration is not hardcoded for specific data types. Instead, it is driven by a well-defined rule set known as the Iterator Protocol.

Understanding the iterator protocol explains how Python loops work internally, why some objects can be looped over while others cannot, and how you can design your own objects to behave like built-in sequences. This knowledge is essential for writing memory-efficient code, building custom data structures, and understanding generators and streams.


What Is the Iterator Protocol in Python?

The iterator protocol is a contract that an object must follow to support iteration. Python does not care about the type of an object when iterating over it. Instead, it checks whether the object follows this protocol.

An object follows the iterator protocol if it implements:

  • __iter__() – returns an iterator object
  • __next__() – returns the next value or raises StopIteration

This design allows Python to treat lists, tuples, files, generators, and custom objects in a uniform way.


How a for Loop Works Internally

A Python for loop is not magic. It is syntactic sugar built on top of the iterator protocol. Consider this loop:

for item in data:
    print(item)

Internally, Python translates it roughly into:

iterator = iter(data)
while True:
    try:
        item = next(iterator)
        print(item)
    except StopIteration:
        break

This explains two critical facts:

  • Iteration depends on iter() and next()
  • Loop termination is controlled by StopIteration

Iterable vs Iterator: Core Conceptual Difference

Although often confused, iterables and iterators are not the same. They serve different roles in the iteration process.

What Is an Iterable?

An iterable is any object that can return an iterator. It implements the __iter__() method.

Examples of iterables:

  • list
  • tuple
  • string
  • set
  • dictionary
  • file objects
data = [1, 2, 3]
iterator = iter(data)

Here, the list is iterable, but not itself an iterator.


What Is an Iterator?

An iterator is an object that produces values one at a time. It represents a stream of data rather than a container.

An iterator must implement both:

  • __iter__() (returns itself)
  • __next__()
iterator = iter([1, 2, 3])
next(iterator)
next(iterator)

Once an iterator is exhausted, it cannot be reset unless explicitly recreated.


Iterable vs Iterator: Comparison Table

Aspect Iterable Iterator
Purpose Produces iterators Produces values
Methods __iter__() __iter__() and __next__()
State No iteration state Maintains iteration state
Reusable Yes No (once exhausted)
Example list, tuple generator, file iterator

Creating a Custom Iterator: Step-by-Step

To understand the iterator protocol deeply, it helps to implement one manually. Below is a simple iterator that counts numbers up to a limit.

class Counter:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.limit:
            raise StopIteration
        self.current += 1
        return self.current

This object is both iterable and an iterator. Each call to next() returns the next value until the limit is reached.


Why Iterators Are Memory Efficient

Iterators generate values on demand instead of storing them all in memory. This makes them ideal for large datasets, streams, and infinite sequences.

Compare this:

numbers = [x for x in range(10000000)]

With this:

numbers = (x for x in range(10000000))

The second version uses a generator (an iterator) and consumes almost no memory.


Real-World Use Cases of the Iterator Protocol

  • Reading large files line by line
  • Streaming data from APIs
  • Processing database cursors
  • Implementing infinite sequences
  • Lazy evaluation pipelines

File objects are a classic example:

with open("data.txt") as file:
    for line in file:
        process(line)

The file object is an iterator that reads one line at a time.


Common Mistakes with Iterables and Iterators

Reusing an Exhausted Iterator

it = iter([1, 2, 3])
list(it)
list(it)  # empty

Once exhausted, an iterator must be recreated.

Confusing Iterable with Iterator

Assuming all iterables maintain state leads to subtle bugs, especially when passing iterators between functions.


Iterator Protocol vs Index-Based Iteration

Unlike index-based loops, iterator-based loops:

  • Do not require random access
  • Work with infinite data
  • Are more memory efficient

This is why Python favors iterators over traditional index-based designs.


Final Thoughts: Why the Iterator Protocol Matters

The iterator protocol is one of Python’s most important abstractions. It enables uniform iteration, lazy evaluation, and composable data pipelines. Understanding the difference between iterables and iterators helps you write efficient, predictable, and Pythonic code.

Once you understand this protocol, concepts like generators, async iteration, and data streaming become much easier to reason about.