The Iterator Protocol
An object is an iterator if it implements two methods: __iter__() (returns the iterator itself) and __next__() (returns the next value, raises StopIteration when done).
# What a for loop actually does:
my_list = [1, 2, 3]
iterator = iter(my_list) # calls __iter__()
print(next(iterator)) # 1 — calls __next__()
print(next(iterator)) # 2
print(next(iterator)) # 3
# next(iterator) # Raises StopIterationCreating a Custom Iterator
Implement __iter__ and __next__ to make any class iterable.
class Countdown:
"""Counts from n down to 1."""
def __init__(self, start):
self.current = start
def __iter__(self):
return self # The iterator IS the object
def __next__(self):
if self.current <= 0:
raise StopIteration
value = self.current
self.current -= 1
return value
for num in Countdown(5):
print(num, end=" ")Iterators vs Generators
Generators are iterators created with yield — much less code for the same result.
# Iterator (verbose)
class EvenNumbers:
def __init__(self, limit):
self.num, self.limit = 0, limit
def __iter__(self): return self
def __next__(self):
if self.num > self.limit: raise StopIteration
val = self.num; self.num += 2; return val
# Generator (same thing, 4 lines)
def even_numbers(limit):
for n in range(0, limit+1, 2):
yield n
print(list(even_numbers(10)))itertools – Powerful Iterator Combinators
The itertools module provides fast, memory-efficient iterator tools.
import itertools
# chain: combine multiple iterables
result = list(itertools.chain([1,2], [3,4], [5]))
print(result) # [1, 2, 3, 4, 5]
# islice: slice any iterator
result = list(itertools.islice(range(100), 5))
print(result) # [0, 1, 2, 3, 4]
# cycle: repeat infinitely
counter = 0
for item in itertools.cycle(["A","B","C"]):
print(item, end=" ")
counter += 1
if counter == 7: breakIterable vs Iterator: What a for Loop Actually Does
These two words get mixed up constantly. An iterable is anything you can loop over (list, string, dict). An iterator is the object that does the actual stepping, remembering its position. A for loop quietly turns the first into the second.
# What `for x in [1,2,3]:` really runs under the hood:
it = iter([1, 2, 3]) # __iter__ → an iterator
while True:
try:
x = next(it) # __next__ → next value
except StopIteration:
break # iterator signals "done" by raising this
| Iterable | Iterator | |
|---|---|---|
| Has | __iter__ | __iter__ + __next__ |
| Example | [1,2,3], "abc" | result of iter(...) |
| Position | none | remembers where it is |
Key consequence: an iterator is one-shot. Once exhausted it stays empty — loop it again and you get nothing. A list is reusable because each for asks it for a fresh iterator. Build custom iterators by defining both dunder methods, or just use a generator (which is an iterator for free).
🏋️ Practical Exercise
Understand the iterator protocol:
- Call
iter()on a list and step through it manually withnext()untilStopIteration. - Write a custom iterator class implementing
__iter__and__next__that counts down from n. - Use it in a
forloop. - Reproduce the same behavior with
itertools.countanditertools.islice.
🔥 Challenge Exercise
Build a custom iterator class Fibonacci(limit) that yields Fibonacci numbers until they exceed a limit, implementing __iter__ and __next__ and raising StopIteration correctly. Then rebuild the same thing as a generator function and compare the code length. Finally, use itertools.takewhile and itertools.count to produce the same sequence in one expression.
📋 Summary
- An iterable can return an iterator via
__iter__; an iterator produces values via__next__. - A
forloop callsiter()then repeatedly callsnext()untilStopIteration. - A custom iterator implements both
__iter__(returning self) and__next__. - Generators are a concise way to create iterators using
yield. - Iterators are lazy and memory-efficient — they produce values one at a time.
itertoolsoffers ready-made iterator building blocks likecount,islice, andtakewhile.
Interview Questions on Iterators
- What is the difference between an iterable and an iterator?
- What two methods make up the iterator protocol?
- What does
StopIterationsignal? - What is the difference between an iterator and a generator?
- How does a
forloop use iterators internally? - What is the
itertoolsmodule used for? - Why are iterators memory-efficient?
Related Topics
FAQ
An iterable is anything you can loop over (list, string, dict) — it implements __iter__. An iterator is the object that actually produces values one at a time via __next__ and remembers its position. Calling iter() on an iterable gives you an iterator.
A generator is a simple way to build an iterator using a function with yield; Python writes __iter__ and __next__ for you. A hand-written iterator class gives more control but requires more code.
StopIteration mean? +It is the exception an iterator raises from __next__ when there are no more items. A for loop catches it automatically to know when to stop.
They compute and hand out one value at a time instead of building the whole sequence in memory. This makes them ideal for large or infinite streams of data.

