Iterators in Python (Part-11)

Posted by

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

guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x