Python Iterators
In this page:
class IteratorClass:
def __iter__(self):
return self
def __next__(self):
# return next value or raise StopIteration
iterator = iter(iterable)
next(iterator)
Iterable vs. Iterator
An iterable is anything you can loop over with a for statement (a list, a string, a dict) -- an iterator is the separate object that actually keeps track of where you are in that sequence and hands back the next element each time it's asked.
Example: Iterable vs. Iterator
numbers = [1, 2, 3]
print(hasattr(numbers, "__iter__")) # lists are iterable
it = iter(numbers)
print(hasattr(it, "__next__")) # the iterator object supports next()
The iter() and next() Functions
iter(some_list) converts an iterable into an iterator object, and calling next(that_iterator) repeatedly retrieves one element at a time, advancing the internal position with each call -- a for loop does exactly this under the hood automatically.
Example: The iter() and next() Functions
numbers = [1, 2, 3]
it = iter(numbers) # converts the list into an iterator
print(next(it)) # advances and returns the first value
print(next(it)) # advances and returns the second value
Creating Custom Iterators
Writing your own iterator class means implementing __iter__ (which conventionally returns self) and __next__ (which computes and returns the next value each time it's called) -- these two methods together are what make an object usable in a for loop.
Example: Creating Custom Iterators
class Counter:
def __init__(self, limit):
self.n = 0
self.limit = limit
def __iter__(self): # returns itself, making the object an iterator too
return self
def __next__(self):
if self.n >= self.limit:
raise StopIteration # signals the loop to stop
self.n += 1
return self.n
for value in Counter(3):
print(value)
The StopIteration Exception
Your __next__ method must raise StopIteration once there are no more items left to produce -- a for loop watches for this specific exception internally and uses it as the signal to stop looping cleanly, rather than treating it as an unhandled error.
Example: The StopIteration Exception
numbers = iter([1])
print(next(numbers))
try:
next(numbers) # no items left
except StopIteration:
print("No more items")
Practical Uses of Iterators
Because an iterator only computes the next value when explicitly asked, it never needs to hold an entire sequence in memory at once -- this lazy evaluation is what makes iterators efficient for processing very large or even effectively infinite sequences of data.
Example: Practical Uses of Iterators
def count_up_to(limit):
n = 1
while n <= limit:
yield n # produces one value at a time, lazily
n += 1
for value in count_up_to(3):
print(value)
- Calling
next()on an iterable such as a list, which raisesTypeError; you neediter(my_list)first. - Forgetting to raise
StopIterationin a custom__next__, which creates an infinite loop. - Reusing an exhausted iterator and getting nothing, because an iterator can only be looped over once.
Chapter Quiz — Complete all 12 topics to unlock
0/12 topics done
Complete these topics first: