Iterators in Python
An iterator in Python is an object that contains a countable number of values. An iterator is an object which implements the iterator protocol, which consists of the methods __iter__()
and __next__()
.
Example: Simple Iterator
# Creating an iterator
my_list = [1, 2, 3, 4]
my_iter = iter(my_list)
print(next(my_iter)) # Output: 1
print(next(my_iter)) # Output: 2
print(next(my_iter)) # Output: 3
print(next(my_iter)) # Output: 4
# next(my_iter) # This will raise StopIteration
Create an Iterator Class
To create an object/class as an iterator, you have to implement the methods __iter__()
and __next__()
to your object.
Example: Creating an Iterator Class
class MyNumbers:
def __init__(self):
self.a = 1
def __iter__(self):
return self
def __next__(self):
if self.a <= 10:
x = self.a
self.a += 1
return x
else:
raise StopIteration
my_class = MyNumbers()
my_iter = iter(my_class)
for num in my_iter:
print(num) # Output: 1 2 3 4 5 6 7 8 9 10
Handling StopIteration
The StopIteration
exception is used to signal the end of the iteration. The __next__()
method raises this exception when there are no more items to return.
Example: Handling StopIteration
class MyNumbers:
def __init__(self):
self.a = 1
def __iter__(self):
return self
def __next__(self):
if self.a <= 5:
x = self.a
self.a += 1
return x
else:
raise StopIteration
my_class = MyNumbers()
my_iter = iter(my_class)
while True:
try:
num = next(my_iter)
print(num)
except StopIteration:
break
Iterators and Iterables
In Python, an iterator is an object which implements the iterator protocol, consisting of the methods __iter__()
and __next__()
. An iterable is an object that can return an iterator.
Iterator Example
Here’s a more detailed example to illustrate an iterator:
class MyNumbers:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
self.current += 1
return self.current
else:
raise StopIteration
# Create an iterator
my_numbers = MyNumbers(5)
my_iter = iter(my_numbers)
for num in my_iter:
print(num) # Output: 1 2 3 4 5
Iterable Example
An iterable is an object that can be iterated over (such as lists, tuples, dictionaries, sets). These objects have an __iter__()
method that returns an iterator.
# Lists are iterable objects
my_list = [1, 2, 3, 4]
for item in my_list:
print(item)
# Strings are also iterable objects
my_string = "Hello"
for char in my_string:
print(char)
Creating an Iterable Class
You can also create a class that acts as an iterable, which returns an iterator object.
class MyNumbers:
def __init__(self, limit):
self.limit = limit
def __iter__(self):
self.current = 0
return self
def __next__(self):
if self.current < self.limit:
self.current += 1
return self.current
else:
raise StopIteration
my_numbers = MyNumbers(5)
for num in my_numbers:
print(num) # Output: 1 2 3 4 5
Custom Iterator with __iter__
and __next__
Creating a custom iterator involves defining both the __iter__()
and __next__()
methods:
class Countdown:
def __init__(self, start):
self.start = start
def __iter__(self):
self.current = self.start
return self
def __next__(self):
if self.current > 0:
self.current -= 1
return self.current
else:
raise StopIteration
# Using the custom iterator
countdown = Countdown(5)
for number in countdown:
print(number) # Output: 4 3 2 1 0
Handling StopIteration
The StopIteration
exception is used to signal the end of iteration. It is raised by the __next__()
method when there are no further items.
class ReverseString:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
# Using the custom iterator
rev_str = ReverseString("Python")
for char in rev_str:
print(char) # Output: n o h t y P
Detailed Example: Creating an Iterator
Here’s a detailed example of how to create an iterator class:
class MyIterator:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
self.current = self.start
return self
def __next__(self):
if self.current <= self.end:
value = self.current
self.current += 1
return value
else:
raise StopIteration
# Usage
my_iter = MyIterator(1, 5)
for num in my_iter:
print(num) # Output: 1 2 3 4 5
Example with Custom StopIteration Handling
This example shows how to use StopIteration
to signal the end of the iteration:
class EvenNumbers:
def __init__(self, max_num):
self.max_num = max_num
def __iter__(self):
self.num = 0
return self
def __next__(self):
if self.num <= self.max_num:
result = self.num
self.num += 2
return result
else:
raise StopIteration
# Usage
even_numbers = EvenNumbers(10)
for num in even_numbers:
print(num) # Output: 0 2 4 6 8 10
Iterator vs Iterable
To reinforce the concepts, remember that an iterator is an object with a __next__()
method, and an iterable is an object with an __iter__()
method that returns an iterator.
# List as an iterable
my_list = [1, 2, 3]
my_iter = iter(my_list)
print(next(my_iter)) # Output: 1
print(next(my_iter)) # Output: 2
print(next(my_iter)) # Output: 3
Creating an Iterable Class
You can also create a class that is both an iterable and an iterator. This is useful when you want to customize the iteration process.
class ReverseString:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
# Usage
rev_str = ReverseString("Hello")
for char in rev_str:
print(char) # Output: o l l e H