Iterators
In Python, iterators are objects that allow you to traverse through a collection (like a list, tuple, or string) and access its elements one at a time, without needing to index them directly. They implement two key methods: __iter__()
and __next__()
. This allows them to be used in loops like for
loops, which makes them very useful in handling large datasets or working with streams of data.
Key Concepts
Iterable: An object that can return an iterator. This includes most collection types like lists, tuples, and dictionaries, which implement the
__iter__()
method.Iterator: An object that keeps track of its current position in an iterable and can return the next item in the collection when requested. It implements the
__next__()
method.
Basic Iterator Example
Here’s a simple example to demonstrate how an iterator works:
# Creating an iterable (a list)
my_list = [1, 2, 3]
# Getting an iterator from the iterable
iterator = iter(my_list)
# Accessing elements one at a time using __next__()
print(next(iterator)) # Output: 1
print(next(iterator)) # Output: 2
print(next(iterator)) # Output: 3
# If we call next() again, it will raise StopIteration because we reached the end
# print(next(iterator)) # Uncommenting this line will raise StopIteration
Explanation:
iter(my_list)
converts the iterable (the list) into an iterator.next(iterator)
retrieves the next element from the iterator. Once all elements are accessed, it raises aStopIteration
exception to signal the end of the iteration.
Iterators in for
Loops
Python’s for
loop automatically handles iteration for you. It internally uses the __iter__()
and __next__()
methods.
Example:
# Using an iterable in a for loop
for number in my_list:
print(number)
This is equivalent to the following code:
iterator = iter(my_list)
while True:
try:
print(next(iterator))
except StopIteration:
break
Custom Iterator Class
You can also create your own iterator by defining a class that implements the __iter__()
and __next__()
methods.
Here’s an example of a custom iterator:
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self # Returning the iterator object itself
def __next__(self):
if self.current > self.end:
raise StopIteration
self.current += 1
return self.current - 1
# Create an instance of the custom iterator
my_iterator = MyIterator(1, 5)
# Using the iterator
for number in my_iterator:
print(number)
Output:
1
2
3
4
5
Explanation:
- The
MyIterator
class has a__next__()
method, which returns the next element in the sequence. - When
current
exceedsend
, theStopIteration
exception is raised to signal the end of the iteration.
Creating an Iterable from an Iterator
You can make an object iterable by defining a class with the __iter__()
method, which returns an iterator. For example:
class CountDown:
def __init__(self, start):
self.start = start
def __iter__(self):
return MyIterator(0, self.start) # Returning the iterator for CountDown
countdown = CountDown(3)
for number in countdown:
print(number)
Output:
0
1
2
3
Infinite Iterators
Sometimes, you may need an infinite sequence, such as generating an infinite series of numbers. You can create an iterator for such cases:
class InfiniteCounter:
def __init__(self, start=0):
self.count = start
def __iter__(self):
return self
def __next__(self):
self.count += 1
return self.count
# Example usage
counter = InfiniteCounter(0)
for i in counter:
print(i)
if i == 5:
break # Stop after printing 5
Output:
1
2
3
4
5
Built-in Iterators in Python
Python comes with several built-in iterators. For instance, you can use the range()
function, which creates an iterator for a sequence of numbers:
# Using range as an iterator
for i in range(5):
print(i)
This will print numbers from 0 to 4.
Summary of Key Methods
__iter__()
: This method returns the iterator object itself. This method is required to make an object iterable.__next__()
: This method returns the next item in the sequence. When there are no more items to return, it raises aStopIteration
exception.
Why Use Iterators?
- Memory efficiency: Instead of storing large collections of data in memory, iterators allow you to generate and process data one item at a time.
- Lazy evaluation: Iterators can generate items on the fly, which is particularly useful when dealing with large datasets or streams of data.