#1 Data Analytics Program in India
₹2,499₹1,499Enroll Now
7 min read
•Question 21 of 41medium

Iterators in Python

Creating custom iterators.

What You'll Learn

  • What iterators and iterables are
  • The iterator protocol (iter and next)
  • Creating custom iterators
  • Built-in iterator functions
  • The itertools module for advanced iteration

Understanding Iterators vs Iterables

An iterable is any object that can return an iterator (has __iter__). An iterator is an object that produces values one at a time (has __next__).

code.pyPython
# Lists are iterables, not iterators
my_list = [1, 2, 3]
print(hasattr(my_list, '__iter__'))  # True
print(hasattr(my_list, '__next__'))  # False

# Get an iterator from the iterable
iterator = iter(my_list)
print(hasattr(iterator, '__next__'))  # True

print(next(iterator))  # 1
print(next(iterator))  # 2
print(next(iterator))  # 3
# next(iterator)  # Raises StopIteration

The Iterator Protocol

To create a custom iterator, implement __iter__ and __next__:

code.pyPython
class Counter:
    """Iterator that counts from start to end (exclusive)."""

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

    def __iter__(self):
        return self  # Iterator returns itself

    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        value = self.current
        self.current += 1
        return value

# Using the iterator
for num in Counter(1, 5):
    print(num)  # 1, 2, 3, 4

# Manual iteration
counter = Counter(10, 13)
print(next(counter))  # 10
print(next(counter))  # 11
print(next(counter))  # 12

Separate Iterator Pattern

For reusable iterables, separate the iterator from the iterable:

code.pyPython
class Team:
    """Iterable that can be iterated multiple times."""

    def __init__(self, members):
        self.members = members

    def __iter__(self):
        return TeamIterator(self.members)

class TeamIterator:
    def __init__(self, members):
        self.members = members
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.members):
            raise StopIteration
        member = self.members[self.index]
        self.index += 1
        return member

team = Team(["Alice", "Bob", "Charlie"])

# Can iterate multiple times
for member in team:
    print(member)

for member in team:  # Works again!
    print(member)

Built-in Iterator Functions

Python provides powerful built-in functions for iteration:

code.pyPython
nums = [1, 2, 3, 4, 5]

# enumerate - get index and value
for i, num in enumerate(nums, start=1):
    print(f"{i}. {num}")  # 1. 1, 2. 2, etc.

# zip - iterate in parallel
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
for name, score in zip(names, scores):
    print(f"{name}: {score}")

# zip_longest for unequal lengths
from itertools import zip_longest
for a, b in zip_longest([1, 2], [1, 2, 3], fillvalue=0):
    print(a, b)  # (1,1), (2,2), (0,3)

# reversed - iterate backwards
for num in reversed(nums):
    print(num)  # 5, 4, 3, 2, 1

# sorted - iterate in sorted order
for num in sorted(nums, reverse=True):
    print(num)  # 5, 4, 3, 2, 1

The itertools Module

Advanced iteration patterns:

code.pyPython
from itertools import (
    count, cycle, repeat,  # Infinite iterators
    chain, islice,         # Chaining and slicing
    permutations, combinations,  # Combinatorics
    groupby                # Grouping
)

# Infinite iterators (use with caution!)
for i in count(10):  # 10, 11, 12, ...
    if i > 12:
        break
    print(i)

# Cycle through items indefinitely
colors = cycle(['red', 'green', 'blue'])
for _ in range(5):
    print(next(colors))  # red, green, blue, red, green

# Chain multiple iterables
combined = chain([1, 2], [3, 4], [5, 6])
print(list(combined))  # [1, 2, 3, 4, 5, 6]

# Slice an iterator
first_five = islice(count(), 5)
print(list(first_five))  # [0, 1, 2, 3, 4]

# Combinations and permutations
print(list(combinations('ABC', 2)))
# [('A', 'B'), ('A', 'C'), ('B', 'C')]

print(list(permutations('AB')))
# [('A', 'B'), ('B', 'A')]

# Group consecutive items
data = [('a', 1), ('a', 2), ('b', 3), ('b', 4)]
for key, group in groupby(data, key=lambda x: x[0]):
    print(key, list(group))
# a [('a', 1), ('a', 2)]
# b [('b', 3), ('b', 4)]

Iterators vs Generators

FeatureIteratorGenerator
DefinitionClass with iter/nextFunction with yield
StateExplicit instance variablesImplicit (Python handles)
MemoryInstance overheadMinimal
Use CaseComplex iteration logicSimple value sequences
code.pyPython
# Iterator version
class Squares:
    def __init__(self, n):
        self.n = n
        self.i = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.i >= self.n:
            raise StopIteration
        result = self.i ** 2
        self.i += 1
        return result

# Generator version (simpler!)
def squares(n):
    for i in range(n):
        yield i ** 2

# Both produce the same result
print(list(Squares(5)))   # [0, 1, 4, 9, 16]
print(list(squares(5)))   # [0, 1, 4, 9, 16]

Interview Tip

When asked about iterators:

  1. Explain iterables (have iter) vs iterators (have next)
  2. Know that for loops call iter() then next() repeatedly
  3. Mention StopIteration signals the end
  4. Recommend generators for simple cases
  5. Know key itertools functions: chain, islice, groupby