Python is known for its elegant syntax and powerful features that make programming intuitive and efficient. One of these features is iterators, which provide a way to access elements of a collection (like lists or tuples) sequentially without exposing the underlying structure. This article delves into what iterators are, how they work, and how you can create your own.
What is an Iterator?
An iterator in Python is an object that implements the iterator protocol, which consists of two methods:
-
__iter__()
— Returns the iterator object itself. -
__next__()
— Returns the next value from the iterator. RaisesStopIteration
when there are no more items.
Any object that has these two methods is considered an iterator.
Example:
numbers = [1, 2, 3]
iterator = iter(numbers)
print(next(iterator)) # Output: 1
print(next(iterator)) # Output: 2
print(next(iterator)) # Output: 3
print(next(iterator)) # Raises StopIteration
Iterables vs Iterators
These terms are often used interchangeably but are fundamentally different:
-
Iterable: Any object capable of returning its members one at a time, e.g., lists, strings, tuples. It implements
__iter__()
but not necessarily__next__()
. -
Iterator: An object that keeps state and produces the next value when you call
next()
.
lst = [10, 20, 30]
iter_obj = iter(lst)
print(hasattr(lst, '__iter__')) # True (Iterable)
print(hasattr(lst, '__next__')) # False
print(hasattr(iter_obj, '__next__')) # True (Iterator)
How for-Loops Use Iterators
Python's for
loop internally uses the iter()
and next()
functions. Here’s how:
for element in [1, 2, 3]:
print(element)
is internally equivalent to:
iterator = iter([1, 2, 3])
while True:
try:
item = next(iterator)
print(item)
except StopIteration:
break
Creating Your Own Iterator
To create a custom iterator, define a class with __iter__()
and __next__()
methods.
Example: Countdown Iterator
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
else:
self.current -= 1
return self.current + 1
for num in Countdown(5):
print(num)
Output:
5
4
3
2
1
Generator vs Iterator
Generators are a simpler way to create iterators using the yield
statement.
Example:
def countdown(n):
while n > 0:
yield n
n -= 1
for number in countdown(5):
print(number)
This generator function automatically creates an iterator and maintains state.
Advantages of Iterators
-
Memory Efficient: Processes items one at a time, especially useful for large datasets.
-
Lazy Evaluation: Items are computed as needed.
-
Composable: Can be easily chained and combined using tools like
itertools
.
Common Iterator Tools
Python’s itertools
module offers powerful tools for iterators.
Examples:
-
count(start, step)
: Infinite counter -
cycle(iterable)
: Infinite cycle through iterable -
chain(iter1, iter2)
: Combines multiple iterables
import itertools
for i in itertools.islice(itertools.count(10, 2), 5):
print(i) # 10, 12, 14, 16, 18
Handling StopIteration Gracefully
When creating custom iterators, always raise StopIteration
when done. However, from Python 3.3+, using return
in a generator automatically raises StopIteration
.
def numbers():
yield 1
yield 2
return
Iterables vs Iterators vs Generators in Python
Feature | Iterable | Iterator | Generator |
---|---|---|---|
Definition | Any Python object capable of returning an iterator | An object that produces elements one at a time | A special kind of iterator created with yield |
Examples | list , tuple , dict , set , str , range |
Object from iter(list) |
Generator function or expression |
Protocol Methods | Must implement __iter__() |
Must implement __iter__() and __next__() |
Automatically implements both via yield |
Exhaustible? | No (can recreate iterators anytime) | Yes (once consumed, can’t reset) | Yes (once consumed, can’t reset) |
Memory Usage | Stores all items in memory | Fetches items one by one | Lazily generates values (memory-efficient) |
Creation | Built-in collections | Call iter() on iterable |
Use yield or generator expression |
Reset/Reusability | Can get a new iterator again | Cannot be reset (must recreate) | Cannot be reset (must recreate) |
Python Iterators – FAQ
❓ What is an iterator in Python?
An iterator is an object that implements the __iter__()
and __next__()
methods, allowing sequential access to elements one at a time.
❓ What is the difference between iterable and iterator?
-
Iterable: Any object you can loop over (e.g., list, tuple, dict, set). Provides an
__iter__()
method that returns an iterator. -
Iterator: The actual object that produces elements one by one via
__next__()
.
❓ Can iterators be reused?
No. Once an iterator is exhausted, it cannot be reset. You need to create a new iterator from the iterable.
nums = [1, 2, 3]
it = iter(nums)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# next(it) -> StopIteration (exhausted)
❓ How is __next__()
different from next()
?
-
__next__()
is the special method defined in the iterator object. -
next(iterator)
is the built-in function that internally callsiterator.__next__()
.
❓ Are generators and iterators the same?
Generators are a special type of iterator. They automatically implement __iter__()
and __next__()
using the yield
keyword, making them easier to write.
❓ Why use iterators instead of lists?
Iterators are memory-efficient because they generate elements one at a time rather than storing all elements in memory at once.
Python Iterators Cheat Sheet
Concept | Example | Output |
---|---|---|
Get iterator from iterable | it = iter([10, 20, 30]) |
– |
Fetch next item | next(it) |
10 |
StopIteration exception | After last element | Error |
Loop automatically consumes iterator | for x in it: print(x) |
20, 30 |
Custom iterator class | Define __iter__() and __next__() |
Produces values |
Convert iterator to list | list(it) |
[10, 20, 30] |
Check if object is iterable | hasattr(obj, '__iter__') |
True/False |
Check if object is iterator | hasattr(obj, '__next__') |
True/False |
Python Iterators Interview Questions
1. What is the difference between iterable, iterator, and generator in Python?
-
Iterable: an object that can return an iterator (
list
,tuple
). -
Iterator: object with
__iter__()
and__next__()
. -
Generator: a special iterator built using
yield
.
2. What happens when an iterator is exhausted?
It raises a StopIteration
exception. Iterators cannot be reset; you must recreate them.
3. How is for
loop internally implemented using iterators?
A for
loop does:
-
Calls
iter(iterable)
→ gets iterator. -
Calls
next(iterator)
repeatedly. -
Stops when
StopIteration
is raised.
4. What is the role of __iter__()
in iterators?
-
For iterables, it returns a new iterator.
-
For iterators themselves,
__iter__()
usually returnsself
.
5. Can you manually iterate without a loop?
Yes, using next()
.
nums = [1, 2, 3]
it = iter(nums)
print(next(it)) # 1
print(next(it)) # 2
6. Is range()
an iterator?
No. range()
is an iterable. It returns an iterator when passed to iter()
.
7. Why are iterators memory efficient?
Because they do not store all items in memory—they generate each item on demand.
Conclusion
Iterators are a cornerstone of Python’s elegant design, enabling powerful and memory-efficient programming patterns. Understanding how to use and implement iterators allows you to work more effectively with collections and custom data streams.
Whether you’re working with built-in sequences or designing your own classes, iterators give you fine-grained control over data traversal.