The Iteration Protocol — iter() & next()
IntermediateEvery for loop is sugar over iter() and next(): an iterable produces an iterator, the iterator yields items until StopIteration — implement two dunders and your class joins in.
Overview
Understanding what for actually does unlocks half of advanced Python. An ITERABLE is anything that returns an iterator from __iter__ (lists, dicts, files, ranges); an ITERATOR is the stateful cursor with __next__ that produces values and raises StopIteration when done. Iterators are consumed once and are themselves iterable. This protocol is why generators, zip, map and files all compose — they all speak the same two-method language.
What for Really Does
for x in obj calls iter(obj) once, then next() repeatedly, catching StopIteration to stop. You can drive it manually — and see why iterators exhaust.
nums = [10, 20, 30]
it = iter(nums) # list (iterable) -> list_iterator
print(next(it)) # 10
print(next(it)) # 20
print(next(it)) # 30
# next(it) # StopIteration!
print(next(it, "END")) # 'END' — default instead of raising
# The for loop, desugared:
it = iter(nums)
while True:
try:
x = next(it)
except StopIteration:
break
print(x)
# Iterators exhaust — a big source of bugs
pairs = zip([1, 2], ["a", "b"])
print(list(pairs)) # [(1,'a'), (2,'b')]
print(list(pairs)) # [] — already consumed!Writing Your Own Iterator
Implement __iter__ (return self) and __next__ (return a value or raise StopIteration). Compare with the 4-line generator version — the reason generators exist.
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for n in Countdown(3):
print(n) # 3 2 1
# The same thing as a generator — Python writes the class for you:
def countdown(start):
while start > 0:
yield start
start -= 1
print(list(countdown(3))) # [3, 2, 1]
# Iterable vs iterator in one line:
# iterable: has __iter__ (can be looped many times, e.g. list)
# iterator: has __next__ too (a one-shot cursor, e.g. generator)Key Points to Remember
- 1Iterable = has __iter__; iterator = also has __next__ and exhausts
- 2for = iter() once + next() until StopIteration
- 3next(it, default) avoids the exception
- 4zip/map/filter/file objects are one-shot iterators — materialize with list() if you need reuse
Interview Questions
Sign in to ask AriaDifference between an iterable and an iterator? Which is a list, which is a generator?
Desugar a for loop into while + iter + next.
Why does iterating the same zip object twice give an empty result the second time?
Ask Aria about The Iteration Protocol — iter() & next()
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.