Iterators and Generators

Let’s explore Iterators and Generators in Python 🌀⚡—with clear explanations, step-by-step code, and practice questions (with solutions)!

🧩 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, or string), one element at a time.

  • Iterable: Any object you can loop over (e.g., list, tuple, set, string).
  • Iterator: An object with two methods: __iter__() and __next__(). It remembers its position during iteration.

How to Use Iterators

numbers = [1, 2, 3]
it = iter(numbers)   # Get iterator from iterable

print(next(it))  # 1
print(next(it))  # 2
print(next(it))  # 3
# print(next(it))  # Raises StopIteration error if no more items

iter() gets the iterator; next() gets the next item until the end is reached.1

⚙️ How Iterators Work in Loops

A for loop in Python automatically creates an iterator from the iterable and uses next() to get each item until StopIteration is raised.2

for num in numbers:
    print(num)

⚡ What is a Generator?

A generator is a special type of iterator, created by a function with the yield keyword.

  • Generator function: Uses yield instead of return.
  • Generator object: Returned by calling a generator function; you can iterate over it just like any other iterator.

Example: Generator Function

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

gen = countdown(3)
print(next(gen))  # 3
print(next(gen))  # 2
print(next(gen))  # 1
# print(next(gen))  # StopIteration error

Each call to next() resumes where the function last yielded.1

🔄 Difference: Iterator vs Generator

FeatureIteratorGenerator
How createdFrom any iterable (using iter())From a function with yield
Memory usageMay store all data in memoryGenerates values on the fly (lazy)
SyntaxClass with __iter__/__next__Function with yield
Exampleiter([^1][^3][^2])def gen(): yield ...

🧠 Generator Expressions

Like list comprehensions, but use () and generate items one by one.

squares = (x*x for x in range(5))
for num in squares:
    print(num)  # 0 1 4 9 16

Efficient for large data—doesn’t build the whole list in memory!3

📝 Practice Questions

1️⃣ Create an iterator for a list and print all values using next().

Solution
lst = [10, 20, 30]
it = iter(lst)
print(next(it))  # 10
print(next(it))  # 20
print(next(it))  # 30

2️⃣ Write a generator function that yields even numbers up to 10.

Solution
def even_numbers():
    n = 0
    while n <= 10:
        yield n
        n += 2

for num in even_numbers():
    print(num)  # 0 2 4 6 8 10

3️⃣ Use a generator expression to print cubes of numbers 1 to 5.

Solution
cubes = (x**3 for x in range(1, 6))
for c in cubes:
    print(c)  # 1 8 27 64 125

4️⃣ What happens if you call next() on an iterator after it’s exhausted?

Answer: You get a StopIteration error.

⭐ Key Points

  • Iterators: Objects that allow you to traverse through all elements, one at a time, using iter() and next().
  • Generators: Special iterators created with functions using yield. More memory-efficient for large data.
  • Generator expressions: Like list comprehensions but lazy (produce items one by one, not all at once).
  • for loops: Automatically use iterators behind the scenes.

Practice using iterators and generators—they’re powerful tools for efficient Python code! 🚀


References:

  • Python-Cheatsheet-2024.pdf (Iterator and Generator section)1
  • Learning_Python.pdf (Iteration, for loops, generator expressions)3
  • itpacs_cafiero.pdf (Iterables, enumerate, behind-the-scenes of iteration)2

  1. Python-Cheatsheet-2024.pdf ↩︎ ↩︎ ↩︎

  2. itpacs_cafiero.pdf ↩︎ ↩︎

  3. Learning_Python.pdf ↩︎ ↩︎