Iterators in Python

Iterators in Python

An iterator in Python is an object that is used to iterate over iterable objects like lists, tuples, dictionaries, and sets. An iterator can be thought of as a pointer to a container’s elements.

To create an iterator, you use the iter() function. To get the next element from the iterator, you use the next() function. When there are no more elements, the next() function raises a StopIteration error.


Example

Let’s use a list as an example to see how an iterator works.

Python

# Create a list
my_list = ['apple', 'banana', 'cherry']

# Create an iterator from the list
my_iterator = iter(my_list)

# Get the first element
print(next(my_iterator))

# Get the second element
print(next(my_iterator))

# Get the third element
print(next(my_iterator))

# Trying to get the next element will raise a StopIteration error
try:
    print(next(my_iterator))
except StopIteration:
    print("No more elements in the iterator.")

Output:

apple
banana
cherry
No more elements in the iterator.

In this example, the my_iterator object keeps track of the current position. Each call to next() moves the iterator to the next element in the list.


How a for loop works with an iterator

The for loop in Python is a convenient way to handle iteration. Under the hood, a for loop automatically creates an iterator for the iterable and calls next() on it until a StopIteration error is raised.

The following for loop:

Python

for fruit in my_list:
    print(fruit)

is equivalent to:

Python

# Get an iterator
my_iterator = iter(my_list)

# Loop indefinitely
while True:
    try:
        # Get the next item
        fruit = next(my_iterator)
        # Print the item
        print(fruit)
    except StopIteration:
        # Break the loop when the iterator is exhausted
        break

What is an Iterator?

An iterator is an object that allows you to traverse through all the elements of a collection (like a list, tuple, dictionary, etc.) one by one. It remembers its state during iteration.

Key Concepts

  1. Iterable: An object that can return an iterator (lists, tuples, strings, etc.)
  2. Iterator: An object that implements __iter__() and __next__() methods

Basic Examples

Example 1: Basic Iterator using iter() and next()

python

# Create a list (iterable)
fruits = ["apple", "banana", "cherry"]

# Get an iterator
fruit_iterator = iter(fruits)

# Get elements one by one
print(next(fruit_iterator))  # Output: apple
print(next(fruit_iterator))  # Output: banana
print(next(fruit_iterator))  # Output: cherry

# This will raise StopIteration error (no more items)
# print(next(fruit_iterator))

Example 2: Using Iterator in a Loop

python

numbers = [1, 2, 3, 4, 5]
num_iterator = iter(numbers)

while True:
    try:
        number = next(num_iterator)
        print(number)
    except StopIteration:
        break
# Output: 1 2 3 4 5

Example 3: Strings are Iterable too!

python

message = "Hello"
char_iterator = iter(message)

print(next(char_iterator))  # Output: H
print(next(char_iterator))  # Output: e
print(next(char_iterator))  # Output: l
print(next(char_iterator))  # Output: l
print(next(char_iterator))  # Output: o

How for-loops Actually Work

Behind the scenes, this:

python

for item in my_list:
    print(item)

Is equivalent to:

python

iterator = iter(my_list)
while True:
    try:
        item = next(iterator)
        print(item)
    except StopIteration:
        break

Creating Your Own Iterator

Example 4: Simple Custom Iterator

python

class CountDown:
    def __init__(self, start):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        else:
            self.current -= 1
            return self.current + 1

# Using our custom iterator
counter = CountDown(3)
for number in counter:
    print(number)
# Output: 3 2 1

Example 5: Number Range Iterator

python

class NumberRange:
    def __init__(self, start, end):
        self.current = start
        self.end = end
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current > self.end:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1

# Create and use iterator
num_range = NumberRange(1, 5)
for num in num_range:
    print(num)
# Output: 1 2 3 4 5

Practical Examples

Example 6: File Reading with Iterator

python

# Files are iterators too!
with open('example.txt', 'w') as f:
    f.write("Line 1\nLine 2\nLine 3")

# Read file line by line using iterator
with open('example.txt', 'r') as file:
    for line in file:  # This uses iterator!
        print(line.strip())
# Output: Line 1, Line 2, Line 3

Example 7: Dictionary Iterator

python

student = {"name": "Alice", "age": 20, "grade": "A"}

# Iterate through keys
for key in student:
    print(key)
# Output: name, age, grade

# Iterate through values
for value in student.values():
    print(value)
# Output: Alice, 20, A

# Iterate through key-value pairs
for key, value in student.items():
    print(f"{key}: {value}")
# Output: name: Alice, age: 20, grade: A

Built-in Functions that Use Iterators

Example 8: Using enumerate()

python

fruits = ["apple", "banana", "cherry"]

for index, fruit in enumerate(fruits):
    print(f"Index {index}: {fruit}")
# Output: Index 0: apple, Index 1: banana, Index 2: cherry

Example 9: Using zip()

python

names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]

for name, age in zip(names, ages):
    print(f"{name} is {age} years old")
# Output: Alice is 25, Bob is 30, Charlie is 35

Key Points to Remember

  1. Iterators are memory efficient: They don’t store all elements in memory at once
  2. One-time use: Once exhausted, they can’t be reused (unless reset)
  3. Lazy evaluation: Elements are computed on-the-fly
  4. Automatic in loops: Python automatically uses iterators in for-loops

Advantages of Iterators

  • Memory efficient: Process large datasets without loading everything into memory
  • Clean code: Simple and readable iteration syntax
  • Flexible: Can create custom iteration behavior
  • Universal: Works with any collection type

Common Built-in Iterables

  • Lists: [1, 2, 3]
  • Tuples: (1, 2, 3)
  • Strings: "hello"
  • Dictionaries: {"a": 1, "b": 2}
  • Sets: {1, 2, 3}
  • Files: open('file.txt')
  • Range: range(5)

Iterators are fundamental to Python and make working with collections efficient and elegant!

Iterator in Python – Interview Q&A

Basic Concept Questions

1. What is an iterator in Python?

Answer:
An iterator is an object that allows traversal through elements of a collection one at a time. It implements two methods:

  • __iter__(): Returns the iterator object itself
  • __next__(): Returns the next element and raises StopIteration when no more elements

Example:

python

my_list = [1, 2, 3]
my_iter = iter(my_list)
print(next(my_iter))  # 1
print(next(my_iter))  # 2

2. What is the difference between an iterable and an iterator?

Answer:

  • Iterable: An object that can return an iterator (has __iter__() method)
  • Iterator: An object that keeps state and produces next values (has both __iter__() and __next__() methods)

All iterators are iterables, but not all iterables are iterators.

3. How does a for-loop work internally?

Answer:
A for-loop automatically:

  1. Calls iter() on the iterable to get an iterator
  2. Calls next() repeatedly until StopIteration is raised

Equivalent code:

python

# This for-loop:
for item in my_list:
    print(item)

# Is equivalent to:
iterator = iter(my_list)
while True:
    try:
        item = next(iterator)
        print(item)
    except StopIteration:
        break

Technical Questions

4. What happens when you call iter() on an iterator?

Answer:
Calling iter() on an iterator returns the iterator itself. This allows iterators to be used in for-loops.

Example:

python

my_list = [1, 2, 3]
my_iter = iter(my_list)
print(my_iter is iter(my_iter))  # True - same object

5. Why can’t you iterate over an iterator multiple times?

Answer:
Iterators are stateful and exhausted after use. Once StopIteration is raised, the iterator can’t be reset.

Example:

python

numbers = [1, 2, 3]
num_iter = iter(numbers)

list(num_iter)  # [1, 2, 3] - exhausts iterator
list(num_iter)  # [] - empty because iterator is exhausted

6. How can you check if an object is an iterator?

Answer:
Check if it has both __iter__() and __next__() methods, or use collections.abc.Iterator.

Example:

python

from collections.abc import Iterator

my_list = [1, 2, 3]
my_iter = iter(my_list)

print(isinstance(my_list, Iterator))  # False - list is iterable but not iterator
print(isinstance(my_iter, Iterator))  # True - this is an iterator

Implementation Questions

7. How would you create a custom iterator?

Answer:
Create a class with __iter__() and __next__() methods.

Example:

python

class SquareNumbers:
    def __init__(self, limit):
        self.current = 1
        self.limit = limit
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current > self.limit:
            raise StopIteration
        result = self.current ** 2
        self.current += 1
        return result

squares = SquareNumbers(3)
for num in squares:
    print(num)  # 1, 4, 9

8. What is the StopIteration exception?

Answer:
StopIteration is raised by the __next__() method to signal that there are no more items to return. For-loops catch this exception to terminate.

Practical Questions

9. How would you create an infinite iterator?

Answer:
Create an iterator that never raises StopIteration.

Example:

python

class InfiniteCounter:
    def __init__(self, start=0):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        result = self.current
        self.current += 1
        return result

# Use with caution - will run forever without break condition
counter = InfiniteCounter()
for i in range(5):  # Limit iteration
    print(next(counter))  # 0, 1, 2, 3, 4

10. How can you iterate over a dictionary?

Answer:
Dictionaries provide several iterator methods:

Example:

python

student = {"name": "Alice", "age": 20, "grade": "A"}

# Iterate keys
for key in student:
    print(key)

# Iterate values
for value in student.values():
    print(value)

# Iterate key-value pairs
for key, value in student.items():
    print(f"{key}: {value}")

Advanced Questions

11. What are the advantages of using iterators?

Answer:

  • Memory efficiency: Process large datasets without loading everything into memory
  • Lazy evaluation: Compute values on-demand
  • Clean syntax: Simple iteration with for-loops
  • Composability: Can chain iterators together

12. How do generators relate to iterators?

Answer:
Generators are a simpler way to create iterators. They automatically implement __iter__() and __next__() methods.

Example:

python

# Generator function
def square_numbers(limit):
    for i in range(1, limit + 1):
        yield i ** 2

# This creates an iterator
squares = square_numbers(3)
print(isinstance(squares, Iterator))  # True

13. What is the difference between iter() and __iter__()?

Answer:

  • iter(): Built-in function that calls __iter__() method
  • __iter__(): Special method that should return an iterator

Example:

python

class MyList:
    def __init__(self, data):
        self.data = data
    
    def __iter__(self):
        return iter(self.data)

my_list = MyList([1, 2, 3])
# iter(my_list) calls my_list.__iter__()

14. How would you reset an iterator?

Answer:
Most iterators can’t be reset. Instead, create a new iterator:

Example:

python

numbers = [1, 2, 3]
iter1 = iter(numbers)
list(iter1)  # [1, 2, 3] - exhausted

# Create new iterator to "reset"
iter2 = iter(numbers)
list(iter2)  # [1, 2, 3] - fresh iterator

15. How can you create an iterator from a function?

Answer:
Use generator functions with yield:

Example:

python

def countdown(n):
    while n > 0:
        yield n
        n -= 1

# This creates an iterator
cd_iter = countdown(3)
print(list(cd_iter))  # [3, 2, 1]

Real-World Scenario Questions

16. How would you process a large file efficiently?

Answer:
Use file iterator to read line by line without loading entire file into memory:

Example:

python

def process_large_file(filename):
    with open(filename, 'r') as file:
        for line in file:  # Uses iterator
            process_line(line)  # Process one line at a time

# Much more memory efficient than:
# with open(filename) as f:
#     lines = f.readlines()  # Loads everything into memory

17. How can you create a pagination iterator?

Answer:
Example:

python

class Paginator:
    def __init__(self, data, page_size=2):
        self.data = data
        self.page_size = page_size
        self.current_page = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        start = self.current_page * self.page_size
        end = start + self.page_size
        
        if start >= len(self.data):
            raise StopIteration
        
        page = self.data[start:end]
        self.current_page += 1
        return page

data = [1, 2, 3, 4, 5, 6]
paginator = Paginator(data, page_size=2)
for page in paginator:
    print(page)  # [1, 2], [3, 4], [5, 6]

18. What’s wrong with this iterator code?

python

class BadIterator:
    def __next__(self):
        return 1

Answer:
It’s missing the __iter__() method. A proper iterator must implement both __iter__() and __next__().

Fixed:

python

class GoodIterator:
def __iter__(self):
return self
def __next__(self):
return 1

Similar Posts

  • Python Variables: A Complete Guide with Interview Q&A

    Here’s a detailed set of notes on Python variables that you can use to explain the concept to your students. These notes are structured to make it easy for beginners to understand. Python Variables: Notes for Students 1. What is a Variable? 2. Rules for Naming Variables Python has specific rules for naming variables: 3….

  • Data hiding

    Data hiding in Python OOP is the concept of restricting access to the internal data of an object from outside the class. 🔐 It’s a way to prevent direct modification of data and protect the object’s integrity. This is typically achieved by using a naming convention that makes attributes “private” or “protected.” 🔒 How Data…

  • Polymorphism

    Polymorphism is a core concept in OOP that means “many forms” 🐍. In Python, it allows objects of different classes to be treated as objects of a common superclass. This means you can use a single function or method to work with different data types, as long as they implement a specific action. 🌀 Polymorphism…

  • For loop 13 and 14th class

    The range() Function in Python The range() function is a built-in Python function that generates a sequence of numbers. It’s commonly used in for loops to iterate a specific number of times. Basic Syntax There are three ways to use range(): 1. range(stop) – One Parameter Form Generates numbers from 0 up to (but not including) the stop value. python for i in range(5):…

  •  Duck Typing

    Python, Polymorphism allows us to use a single interface (like a function or a method) for objects of different types. Duck Typing is a specific style of polymorphism common in dynamically-typed languages like Python. What is Duck Typing? 🦆 The name comes from the saying: “If it walks like a duck and it quacks like…

  • Tuples

    In Python, a tuple is an ordered, immutable (unchangeable) collection of elements. Tuples are similar to lists, but unlike lists, they cannot be modified after creation (no adding, removing, or changing elements). Key Features of Tuples: Syntax: Tuples are defined using parentheses () (or without any brackets in some cases). python my_tuple = (1, 2, 3, “hello”) or (without…

Leave a Reply

Your email address will not be published. Required fields are marked *