CodeNebula
Python🔧Core Languagebeginner

Iterables, Iterators, and the Iterator Protocol

Python · Core Language · Run with python3

Explanation

Python's `for` loop works on any *iterable* — an object that implements `__iter__()` returning an *iterator*. An iterator implements `__next__()` raising `StopIteration` when exhausted. Built-ins like `list`, `dict`, `str`, `range`, files, and generators are all iterables. `iter(obj)` and `next(obj)` are the protocol functions. Understanding this protocol explains how `for`, comprehensions, `zip`, `map`, `filter`, and `itertools` all interoperate.

Real-World Scenario

A custom `DatabaseCursor` class that lazily fetches rows in batches from a remote database can implement the iterator protocol. Callers use it with a normal `for` loop — they don't need to know about the batching. ```python class CountUp: """Iterator that counts from start to stop.""" def __init__(self, start: int, stop: int): self._current = start self._stop = stop def __iter__(self): return self # Iterator is its own iterable def __next__(self): if self._current >= self._stop: raise StopIteration val = self._current self._current += 1 return val for n in CountUp(1, 6): print(n, end=" ") # 1 2 3 4 5 # Equivalent generator expression gen = (n for n in range(1, 6)) print(list(gen)) # [1, 2, 3, 4, 5] ```

Quick Snippet

snippet.py
class CountUp:
    """Iterator that counts from start to stop."""
    def __init__(self, start: int, stop: int):
        self._current = start
        self._stop = stop

    def __iter__(self):
        return self  # Iterator is its own iterable

    def __next__(self):
        if self._current >= self._stop:
            raise StopIteration
        val = self._current
        self._current += 1
        return val

for n in CountUp(1, 6):
    print(n, end=" ")  # 1 2 3 4 5

# Equivalent generator expression
gen = (n for n in range(1, 6))
print(list(gen))  # [1, 2, 3, 4, 5]

Full Example

iterators.py
# Compile: python3 iterators.py
# Output:
# 1 2 3 4 5
# [1, 2, 3, 4, 5]

class CountUp:
    def __init__(self, start: int, stop: int):
        self._current = start
        self._stop = stop

    def __iter__(self):
        return self

    def __next__(self):
        if self._current >= self._stop:
            raise StopIteration
        val = self._current
        self._current += 1
        return val

for n in CountUp(1, 6):
    print(n, end=" ")
print()

# Generator expression — equivalent, shorter
gen = (n for n in range(1, 6))
print(list(gen))

# Protocol functions
it = iter([10, 20, 30])
print(next(it))   # 10
print(next(it))   # 20
print(next(it, "done"))  # 30
print(next(it, "done"))  # done (default, no StopIteration)

# iter() with sentinel
import io
f = io.StringIO("a\nb\n\n")
lines = list(iter(f.readline, ""))  # reads until empty string
print(lines)
Run command
python3 iterators.py