Overview
Python iterator objects retrieve one element at a time from an iterable during the iteration process. They work behind features such as for loops and the next() function.
Understanding iterator objects helps explain how Python processes data efficiently, manages iteration, and supports advanced features such as generators and lazy evaluation.
This tutorial explains what Python iterator objects are, how they work, their characteristics, how they differ from iterable objects, and why they are memory efficient.
Quick Navigation
Use the links below to quickly navigate through this tutorial:
💡 Tip: If you’re new to this topic, start with our Python Iteration Tutorial: Learn Python Iteration with Examples to build a strong foundation before exploring this concept.
Introduction: What Is an Iterator in Python?
A Python iterator object is an object that returns one element at a time from an iterable. Instead of providing all elements at once, it supplies the next available element whenever Python requests it.
An iterator is created from an iterable by using the iter() function. Once the iterator is created, Python retrieves each successive element by calling the next() function until no elements remain.
This entire process happens automatically inside a for loop, although you can also perform it manually when needed.
Simple Example
numbers = [10, 20, 30]
iterator = iter(numbers)
print(next(iterator))
print(next(iterator))
print(next(iterator))
# Output
10
20
30
Here, numbers is an iterable object. Calling iter() creates an iterator, and each call to next() returns the next element from the list.
How an Iterator Works
The iterator keeps track of which element should be returned next. Each time next() is called, the iterator moves forward by one position.
Iterable
│
▼
iter()
│
▼
Iterator
│
▼
next()
│
▼
Next Element
Once every element has been returned, the iterator signals that the iteration is complete by raising a StopIteration exception. You’ll learn more about this behavior later in this tutorial.
Remember: An iterable stores or generates data, while an iterator retrieves that data one element at a time.
Characteristics of Python Iterators
Although iterator objects are created from different kinds of iterables, they all behave in a similar way. Understanding these characteristics will help you see how Python retrieves data during iteration.
The following are the main characteristics of Python iterator objects:
- Produces One Element at a Time
- Maintains Its Current Position
- Can Be Created from an Iterable
- Implements
__next__() - Raises
StopIteration
Let’s explore each characteristic with a simple example.
1. Produces One Element at a Time
Unlike a list, an iterator does not return all its elements together. Instead, it returns only one element each time next() is called.
fruits = ["Apple", "Banana", "Mango"]
iterator = iter(fruits)
print(next(iterator))
print(next(iterator))
# Output
Apple
Banana
Explanation: Each call to next() retrieves only one item from the iterator.
2. Maintains Its Current Position
An iterator remembers where it is during iteration. After returning one element, it automatically moves to the next position instead of starting from the beginning again.
numbers = [100, 200, 300]
iterator = iter(numbers)
print(next(iterator))
print(next(iterator))
# Output
100
200
Explanation: The second call returns 200 because the iterator remembers that 100 has already been processed.
3. Can Be Created from an Iterable
An iterator is not created manually from scratch in most cases. Instead, Python creates an iterator from an iterable by using the iter() function. Once created, the iterator returns one element at a time when next() is called.
fruits = ["Apple", "Banana", "Mango"]
iterator = iter(fruits)
print(next(iterator))
print(next(iterator))
print(next(iterator))
# Output:
Apple
Banana
Mango
Explanation: Here, the list fruits is an iterable. The iter() function creates an iterator from the list, and each call to next() retrieves the next element in sequence until all elements have been returned.
4. Implements __next__()
Every Python iterator object implements the special __next__() method. The built-in next() function internally calls this method to retrieve the next available element.
numbers = [1, 2, 3]
iterator = iter(numbers)
print(next(iterator))
# Output
1
Explanation: Although Python internally uses the __next__() method, you will usually work with the simpler next() function.
5. Raises StopIteration
An iterator continues returning elements until none remain. When there are no more elements to return, Python raises a StopIteration exception to indicate that the iteration has finished.
numbers = [5]
iterator = iter(numbers)
print(next(iterator))
try:
print(next(iterator))
except StopIteration:
print("StopIteration")
# Output
5
StopIteration
Explanation: The first call to next() returns the only element in the iterator. The second call tries to retrieve another element, but the iterator has already been exhausted. Python raises a StopIteration exception, which is caught by the try...except block and displayed as StopIteration.
Note: In normal Python programs, a for loop catches the StopIteration exception automatically. You usually see it only when calling next() manually after all elements have been consumed.
Iterable vs Iterator in Python
The terms iterable and iterator are closely related, but they do not mean the same thing. This is one of the most common areas of confusion for beginners.
An iterable is an object that can produce an iterator, while an iterator is the object that actually retrieves one element at a time during iteration.
In simple terms, an iterable is the source of the data, and an iterator is the object that moves through that data.
Comparison Table
| Iterable | Iterator |
|---|---|
| Stores or provides a collection of values. | Retrieves one value at a time from an iterable. |
Can create an iterator using iter(). |
Is created from an iterable. |
Can be used in a for loop. |
Supplies elements during the loop. |
| Does not keep track of iteration progress. | Keeps track of its current position. |
Examples: list, tuple, string, dictionary, set, range(). |
Examples: objects returned by iter(). |
Simple Example
numbers = [10, 20, 30]
iterator = iter(numbers)
print(next(iterator))
print(next(iterator))
# Output
10
20
In this example, numbers is the iterable, while iterator is the iterator created from it. Each call to next() retrieves the next available element.
When Each Is Used
In everyday Python programming, you usually work directly with iterables such as lists, tuples, strings, dictionaries, and sets. Whenever you write a for loop, Python automatically creates an iterator behind the scenes.
You normally work with an iterator only when you need complete control over the iteration process, such as retrieving one element at a time by using next().
Remember: Every iterator comes from an iterable, but an iterable itself is not an iterator.
Memory Efficiency of Python Iterators
One of the biggest advantages of Python iterator objects is that they are memory efficient. Instead of loading all values into memory at once, an iterator produces only the next value when it is needed.
This approach allows Python to process large collections of data without consuming unnecessary memory.
Why Iterators Save Memory
A list stores every element in memory from the moment it is created. An iterator works differently. It produces one element at a time and does not need to keep every value ready in memory.
Because of this behavior, iterators are especially useful when working with large datasets, files, or sequences that contain millions of values.
Iterator vs List
| List | Iterator |
|---|---|
| Stores every value in memory. | Produces values one at a time. |
| Requires more memory for large collections. | Uses much less memory. |
| Can be accessed repeatedly. | Moves forward as values are retrieved. |
| Suitable for small and medium-sized collections. | Ideal for processing large amounts of data. |
Why range() Is Memory Efficient
Many beginners assume that range() stores every number in memory like a list. In reality, a range() object provides numbers only when they are requested during iteration.
numbers = range(1, 1000000)
Even though this sequence represents one million numbers, Python does not store all of them at once. Instead, the values are produced as they are needed during iteration.
Real-World Example
Suppose you need to process a log file that contains millions of lines. Loading the entire file into memory could consume a large amount of RAM.
Instead, Python reads one line at a time through an iterator, processes it, and then moves to the next line. This allows very large files to be processed efficiently without exhausting available memory.
Lazy Evaluation in Python Iterators
One reason Python iterators are memory efficient is that they use a technique called lazy evaluation.
Instead of generating every value immediately, an iterator produces each value only when it is requested. This means Python performs only the work that is actually needed.
What Is Lazy Evaluation?
Lazy evaluation means that values are created only when they are required, not before.
For example, when you retrieve one element by calling next(), Python generates only that single element. The remaining values are produced later as they are requested.
Why Python Uses It
Generating values only when they are needed helps Python use memory efficiently and avoids unnecessary computation. This approach also makes it possible to work with very large or even infinite sequences.
Benefits of Lazy Evaluation
- Uses less memory.
- Processes large datasets efficiently.
- Avoids generating unnecessary values.
- Improves performance in many situations.
- Makes generators and iterators possible.
Example
numbers = iter(range(1, 6))
print(next(numbers))
print(next(numbers))
print(next(numbers))
# Output
1
2
3
The iterator does not prepare all five numbers in advance. Instead, each number is produced only when next() requests it. This is a simple example of lazy evaluation in Python.
Remember: Lazy evaluation means “produce values only when they are needed.” This is one of the main reasons why Python iterators are efficient for processing large amounts of data.
Common Beginner Mistakes
When learning about Python iterator objects, beginners often misunderstand how iterators work because most iteration is handled automatically by for loops. Learning these common mistakes will help you avoid unexpected behavior and better understand Python’s iteration process.
The following are some of the most common mistakes beginners make when working with Python iterators:
- Thinking Iterables and Iterators Are the Same
- Calling
next()After Iteration Ends - Expecting an Iterator to Restart Automatically
- Forgetting About
StopIteration
Let’s look at each of these mistakes and understand how to avoid them.
1. Thinking Iterables and Iterators Are the Same
Although the two terms are closely related, they describe different objects. An iterable provides the data, while an iterator retrieves one element at a time from that data.
For example, a list is an iterable, but the object returned by iter(list) is an iterator.
2. Calling next() After Iteration Ends
Once an iterator has returned all of its elements, there are no more values left to retrieve. Calling next() again raises a StopIteration exception.
numbers = [10]
iterator = iter(numbers)
print(next(iterator))
try:
print(next(iterator))
except StopIteration:
print("StopIteration")
# Output
10
StopIteration
Explanation: The first call to next() returns the only element in the iterator. The second call attempts to retrieve another value, but the iterator has already been exhausted. Python raises a StopIteration exception, which is caught by the try...except block and displayed as StopIteration.
for loop, Python handles the StopIteration exception automatically. You normally encounter it only when calling next() manually.
3. Expecting an Iterator to Restart Automatically
An iterator remembers its current position. After it reaches the end, it does not automatically return to the beginning.
numbers = [1, 2, 3]
iterator = iter(numbers)
for value in iterator:
print(value)
for value in iterator:
print(value)
The second loop produces no output because the iterator has already been exhausted.
If you want to iterate again, create a new iterator by calling iter() on the iterable.
4. Forgetting About StopIteration
Every iterator eventually reaches the end of its data. At that point, Python raises a StopIteration exception to indicate that no more elements are available.
Many beginners assume the iterator will continue returning values forever. In reality, reaching the end of the sequence is the normal behavior of every iterator.
Fortunately, when you use a for loop, Python catches this exception automatically, making iteration simple and safe.
Key Takeaways: Python Iterator Objects
Here are the key concepts to remember about Python iterator objects:
- Python iterator objects retrieve one element at a time from an iterable.
- An iterator is created from an iterable by using the
iter()function. - The
next()function returns the next available element from an iterator. - Iterators remember their current position and continue from where they last stopped.
- When no elements remain, the iterator raises a
StopIterationexception. - Iterators are memory efficient because they produce values only when they are needed.
- Lazy evaluation allows Python to process large datasets without loading everything into memory.
- Understanding iterators makes it easier to learn generators and advanced iteration techniques.