Iterators in Python (Part-11)

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

Related Posts

Package your Python code as Wheel File (Part-20)

Package your Python code as Wheel File If you want to share code with others or create libraries for your code so other can use your code….

Read More

map() function in Python (Part-19)

Using map() Function in Python The map() function in Python is used to apply a function to all the items in an input list. Here are some…

Read More

User Input | input() function |File Handling| open() function in Python (Part-18)

Using input() in Python 3.6 and later When you run this code, it will prompt you to enter a username and then print the entered username. Using…

Read More

Try…Except in Python | Exception handling in Python (part-17)

Exception Handling When an error occurs, or exception as we call it, Python will normally stop and generate an error message. These exceptions can be handled using…

Read More

PIP in Python (Part-16)

What is PIP? PIP is a package manager for Python. It allows you to install and manage additional libraries and dependencies that are not included in the…

Read More

JSON in Python (Part-15)

JSON in Python JSON (JavaScript Object Notation) is a lightweight data interchange format that’s easy for humans to read and write, and easy for machines to parse…

Read More
guest
0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x
()
x