Iterator & Iterables

In Python, Iterators and Iterables are key concepts for working with sequences of data. They enable looping through a collection of elements, such as lists, tuples, or even custom objects, without requiring the entire sequence to be loaded in memory.

1. Iterables

An Iterable is any Python object that can return an iterator. In practical terms, an iterable is any object that can be looped over (e.g., using a for loop). To be iterable, an object must implement the __iter__() method, which returns an iterator.

Common iterables in Python include:

  • Lists ([1, 2, 3])
  • Tuples ((1, 2, 3))
  • Dictionaries ({‘a’: 1, ‘b’: 2})
  • Sets ({1, 2, 3})
  • Strings (“abc”)

Example of an Iterable

numbers = [1, 2, 3]  # List is an iterable
for number in numbers:
    print(number)

# Output:
# 1
# 2
# 3

In this example: - The list numbers is iterable because it can be looped over with a for loop.

Key Point: - When you use a for loop on an iterable, Python internally calls the __iter__() method to get an iterator and then uses it to fetch each element one at a time.

2. Iterators

An Iterator is an object that represents a stream of data. It produces each value from an iterable one at a time, as requested, rather than storing all elements in memory. To be an iterator, an object must implement two methods:

  • __iter__(): Returns the iterator object itself.
  • __next__(): Returns the next element in the sequence, and raises a StopIteration exception when there are no more elements.

In Python, iterators are lazy, meaning they generate items only when needed. This is useful for working with large datasets, as iterators don’t load all items into memory at once.

Example of an Iterator

numbers = [1, 2, 3]  # List is an iterable
iterator = iter(numbers)  # Get an iterator from the list

print(next(iterator))  # Output: 1
print(next(iterator))  # Output: 2
print(next(iterator))  # Output: 3

# print(next(iterator))  # Raises StopIteration since there are no more items

In this example: - iter(numbers) returns an iterator for the list numbers. Each call to *next(iterator)*fetches the next element in the sequence.

Key Points: The next() function internally calls the iterator’s __next__() method. Once the iterator is exhausted, next() raises a StopIteration exception, signaling there are no more items.

Custom Iterator Example

You can create your iterator by defining a class with the __iter__() and __next__() methods.

class Counter:
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self  # The iterator object returns itself
    
    def __next__(self):
        if self.current < self.end:
            self.current += 1
            return self.current - 1
        else:
            raise StopIteration

# Usage
counter = Counter(1, 5)
for number in counter:
    print(number)

# Output:
# 1
# 2
# 3
# 4

In this example, Counter is an iterator that counts from a start value up to an end value. __iter__() returns the iterator object itself. __next__() increments the current and returns it until it reaches the end, then raises StopIteration.

Track your progress

Mark this subtopic as completed when you finish reading.