DS in Python 1: Iterators
When working with Python, we often process collections such as lists, tuples, dictionaries, files, NumPy arrays, or even huge datasets.
Consider this:
numbers = [10, 20, 30, 40]
for number in numbers:
print(number)
'''
Output:
10
20
30
40
'''This looks simple. But have you ever wondered:
How does Python know which element comes next?
The answer is: Iterators.
Iterators are one of the fundamental concepts behind Python’s for loops, generators, file processing, and many memory-efficient Data Science workflows.
1. What Is an Iterator?
An iterator is an object that allows us to access elements one at a time.
Instead of loading and processing everything simultaneously, an iterator gives us the next value whenever we ask for it.
For example:
numbers = [10, 20, 30, 40]
iterator = iter(numbers)
print(next(iterator))
print(next(iterator))
print(next(iterator))
'''Output:
10
20
30
'''Each call to next() moves the iterator forward.
numbers -> [10, 20, 30, 40]
next()
↓
10
next()
↓
20
next()
↓
30An important point is that the iterator remembers its current position.
After consuming 10, Python knows that the next value should be 20.
2. Iterable vs Iterator
This is one of the most important concepts to understand.
Many people confuse an iterable with an iterator.
What is an Iterable?
An iterable is an object that contains a collection of values and can be converted into an iterator.
Common iterables include:
list
tuple
string
dictionary
set
file
rangeExample:
numbers = [1, 2, 3, 4]The list is an iterable.
But we cannot directly use next() on a list:
numbers = [1, 2, 3]
next(numbers)This raises an error:
TypeError: 'list' object is not an iteratorWe first need to create an iterator.
numbers = [1, 2, 3]
iterator = iter(numbers)
print(next(iterator))
#Output:
#1What is an Iterator?
An iterator is an object that:
- Keeps track of its current position
- Returns the next item using
next() - Raises
StopIterationwhen no elements remain
Example:
numbers = [10, 20, 30]
iterator = iter(numbers)
print(next(iterator))
print(next(iterator))
print(next(iterator))
'''OUTPUT:
10
20
30
'''Now there are no values left.
print(next(iterator))Python raises:
StopIterationThe relationship can be visualized like this:
Iterable (list, dictionary, ... )
|
| iter() {Convert the iterable to iterator using iter()}
v
Iterator
|
| next() {Use this next() function to access one element at a time}
v
Item 1 → Item 2 → Item 3 → StopIteration3. The iter() Function
Python provides the iter() function to convert an iterable to an iterator
numbers = [10, 20, 30]
iterator = iter(numbers)
print(iterator) # 10Another example using a string:
text = "DATA"
iterator = iter(text)
print(next(iterator)) # D
print(next(iterator)) # AAnything which is an iterable can be converted to an iterator using iter()
4. The next() function
The next() function retrieves the next item from an iterator.
numbers = [100, 200, 300]
iterator = iter(numbers)
print(next(iterator)) #100
print(next(iterator)) #200
print(next(iterator)) #300Conceptually:
Iterator initially
[100, 200, 300]
↑
next()
--------------------------
After first next()
[100, 200, 300]
↑
next()
--------------------------
After second next()
[100, 200, 300]
↑
next()Once every item has been consumed, Python raises StopIteration.
5. How does a ‘for’ Loop use an Iterator?
When we write:
numbers = [10, 20, 30]
for number in numbers:
print(number)Python internally does something similar to this:
iterator = iter(numbers)
while True:
try:
number = next(iterator)
print(number)
except StopIteration:
breakSo:
for number in numbers:
print(number)is essentially Python automatically doing:
iter()
↓
next()
↓
next()
↓
next()
↓
StopIteration
↓
StopThis is why for loops can work with lists, tuples, strings, files, generators, and many other objects.
6. The Iterator Protocol
For an object to behave as an iterator, it must follow Python’s iterator protocol.
An iterator should implement two methods:
__iter__()
This method returns the iterator object.
__next__()
This method returns the next value.
Get Abhishek Jain’s stories in your inbox
Join Medium for free to get updates from this writer.
When no more values are available, it should raise: StopIteration
Conceptually:
class MyIterator:
def __iter__(self):
return self
def __next__(self):
# Return next item
# If finished:
raise StopIteration7. Creating a Custom Iterator
Let’s create our own iterator.
Suppose we want an iterator that returns numbers from 1 to 5.
class NumberIterator:
def __init__(self):
self.current = 1
def __iter__(self):
return self
def __next__(self):
if self.current > 5:
raise StopIteration
value = self.current
self.current += 1
return valueNow we can use it.
iterator = NumberIterator()
print(next(iterator)) #1
print(next(iterator)) #2
print(next(iterator)) #3Or use it inside a for loop:
for number in NumberIterator():
print(number)8. Iterable Object vs Iterator Object
We can create a custom iterable and a separate iterator.
For example:
class NumberCollection:
def __init__(self, numbers):
self.numbers = numbers
def __iter__(self):
return NumberIterator(self.numbers)
class NumberIterator:
def __init__(self, numbers):
self.numbers = numbers
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.numbers):
value = self.numbers[self.index]
self.index += 1
return value
raise StopIteration
collection = NumberCollection([10, 20, 30])
for number in collection:
print(number)
#10
#20
#30An iterale is an object that can return an iterator
An iterable implements __iter__() and can return an iterator. An iterator implements both __iter__() and __next__().
Type 1: Sequence Iterators
Sequences such as lists, tuples, strings, and ranges can produce iterators.
List Iterator
numbers = [10, 20, 30]
iterator = iter(numbers)
print(next(iterator)) #10Tuple Iterator
values = (1, 2, 3)
iterator = iter(values)
print(next(iterator)) # 1String Iterator
text = "AI"
iterator = iter(text)
print(next(iterator)) #A
print(next(iterator)) #IRange Iterator
iterator = iter(range(1, 5))
for value in iterator:
print(value)
#1
#2
#3
#4Infinite Iterators
Some iterators can theoretically generate values forever.
For example, using itertools.count():
import itertools
counter = itertools.count()
print(next(counter)) #0
print(next(counter)) #1
print(next(counter)) #2It can continue:
3
4
5
...Another example:
counter = itertools.count(start=10, step=5)
for _ in range(5):
print(next(counter))
'''
10
15
20
25
30
'''Infinite iterators should be used carefully.
This will never stop:
for value in itertools.count():
print(value)A Simple Example to Remember
Here’s perhaps the simplest way to understand an iterator:
numbers = [10, 20, 30]
iterator = iter(numbers)
while True:
try:
number = next(iterator)
print(number)
except StopIteration:
breakOutput:
10
20
30The iterator remembers its position.
After returning 10, it knows that 20 is next.
After returning 20, it knows that 30 is next.
After returning 30, it knows that there are no more values.
Advantages of Iterators
Iterators provide several important benefits.
1. Memory Efficient
They allow us to process values one at a time instead of storing everything in memory.
2. Good for Large Data
They are particularly useful when working with large files, datasets, or streams.
3. Lazy Evaluation
Values can be produced only when they are needed.
This is called lazy evaluation.
4. Simple Looping
Python’s for loop works naturally with iterators.
5. Reusable Concept
The same iteration mechanism works with many different data sources.
One Important Point: Iterators Are Usually Exhausted
An iterator moves forward.
For example:
numbers = [10, 20, 30]
iterator = iter(numbers)
print(list(iterator)) # [10, 20, 30]If you try to use the same iterator again:
print(list(iterator))you’ll get:
[]Why?
Because the iterator has already reached the end.
If you want to iterate again, we will have to create a new iterator:
iterator = iter(numbers)
print(list(iterator)) # [10, 20, 30]







