Python Iterators — The Iterator Protocol Explained
Iteration is fundamental to Python. Every for loop uses iterators under the hood. Understanding the iterator protocol gives you power over custom iteration.
Learning Objectives
- Understand the iterator protocol (
__iter__and__next__) - Create custom iterators with classes
- Use the
iter()andnext()built-in functions - Implement infinite sequences safely
The Iterator Protocol
# Every iterable has __iter__ that returns an iterator
# Every iterator has __next__ that returns the next value
# Built-in iterables
nums = [1, 2, 3]
it = iter(nums) # Calls nums.__iter__()
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# next(it) # Raises StopIteration
Custom Iterator Class
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current < 0:
raise StopIteration
value = self.current
self.current -= 1
return value
# Usage
for num in CountDown(5):
print(num) # 5, 4, 3, 2, 1, 0
Custom Iterable Class
class NumberRange:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return NumberRangeIterator(self.start, self.end)
class NumberRangeIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
value = self.current
self.current += 1
return value
# Can iterate multiple times
nums = NumberRange(0, 5)
print(list(nums)) # [0, 1, 2, 3, 4]
print(list(nums)) # [0, 1, 2, 3, 4] — works again!
Generator Functions (Simpler Iterators)
def countdown(n):
while n >= 0:
yield n
n -= 1
# This is equivalent to the class-based CountDown above
for num in countdown(5):
print(num)
# Generators are iterators
gen = countdown(3)
print(next(gen)) # 3
print(next(gen)) # 2
Infinite Iterators
from itertools import count, cycle, repeat
# Infinite counter
for i in count(10):
if i > 15:
break
print(i) # 10, 11, 12, 13, 14, 15
# Cycle through items
colors = cycle(["red", "green", "blue"])
for _ in range(6):
print(next(colors)) # red, green, blue, red, green, blue
# Repeat a value
ones = repeat(1, 5)
print(list(ones)) # [1, 1, 1, 1, 1]
Consuming Iterators
# list() consumes entire iterator
nums = [1, 2, 3]
print(list(iter(nums))) # [1, 2, 3]
# enumerate, zip, map, filter all return iterators
scores = [95, 87, 91, 78, 85]
for i, score in enumerate(scores):
print(f"Student {i}: {score}")
# itertools for advanced consumption
from itertools import islice, takewhile, dropwhile
# Take first 3
print(list(islice(count(), 3))) # [0, 1, 2]
# Take while condition is true
print(list(takewhile(lambda x: x < 5, count()))) # [0, 1, 2, 3, 4]
Key Takeaways
- Iterators implement
__iter__and__next__ - Generators are the easiest way to create iterators
- Iterators are consumed once — they are not reusable
StopIterationsignals the end of iteration- Use
itertoolsfor advanced iteration patterns