Iterables, like lists, strings, and dicts, and iterators are related but distinct: an iterable is anything you can get an iterator from, by calling iter() on it, while an iterator is the object that actually produces values one at a time via next(), and remembers its position between calls. A for loop works by calling iter() on whatever you're looping over to get an iterator, then repeatedly calling next() on it until StopIteration is raised, which the loop catches internally to know when to stop — this is exactly the mechanism that makes for loops work uniformly across every different kind of iterable.
1Understanding Iterators
Iterables, like lists, strings, and dicts, and iterators are related but distinct: an iterable is anything you can get an iterator from, by calling iter() on it, while an iterator is the object that actually produces values one at a time via next(), and remembers its position between calls. A for loop works by calling iter() on whatever you're looping over to get an iterator, then repeatedly calling next() on it until StopIteration is raised, which the loop catches internally to know when to stop — this is exactly the mechanism that makes for loops work uniformly across every different kind of iterable.
An iterator is exhausted after one full pass — once next() has raised StopIteration, calling next() again just raises StopIteration forever; you need a fresh iterator, usually by calling iter() again on the original iterable, to go through the values a second time.
numbers = [10, 20, 30]
it = iter(numbers)
print(next(it))
print(next(it))
print(next(it))2Practical Example
Here is a real-world application of Iterators showing how it is used in production Python code.
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)3Best Practices
Follow these guidelines when working with Iterators:
1. Implement __iter__ and __next__ on a custom class if you want instances to work directly with for loops and other iteration contexts
2. Remember that an iterator, unlike many iterables like a list, is exhausted after one pass — get a fresh one if you need to iterate again
3. Use the two-argument form of iter() to keep calling a function until it returns a specific sentinel value, a lesser-known but useful pattern
Tip: An iterator is exhausted after one full pass — once next() has raised StopIteration, calling next() again just raises StopIteration forever; you need a fresh iterator, usually by calling iter() again on the original iterable, to go through the values a second time.
numbers = [10, 20, 30]
it = iter(numbers)
print(next(it))
print(next(it))
print(next(it))