Iterators & Generators — Practice Questions
50 questions. Try each one yourself before checking the answer.
Given numbers = [1, 2, 3], create an iterator from it using iter() and print its type().
Using the iterator from Q1, call next() on it three times, printing each result.
Exhaust a 2-item iterator with two next() calls, then call next() a third time and catch the StopIteration it raises.
Using an exhausted iterator, call next(iterator, "done") to get a default value instead of a StopIteration error.
Write a generator function count_to_three() that yields 1, then 2, then 3.
Call count_to_three() (from Q5) WITHOUT looping over it, and print the result directly to show it's a generator object, not the yielded values.
Create a generator expression for the squares of 1 to 5 (using () instead of []), then convert it to a list to see its values.
Manually call next() on the generator returned by count_to_three() (from Q5) twice, then finish the rest with a for loop.
Given numbers = [1, 2, 3], show that a for loop works directly on the LIST, but you can also manually build an iterator from it with iter() first.
Use a generator expression directly inside sum() to add up the squares of 1 to 5, without ever building a list.
Write a generator function squares_up_to(n) that yields the square of each number from 1 to n, using a loop inside.
Write an infinite generator count_up_from(start) using while True and yield, then manually pull only the first 5 values with next().
Compare a list comprehension [n for n in range(5)] and a generator expression (n for n in range(5)) — print both and their types, and explain the difference in a comment.
Given words = ["apple", "kiwi", "banana", "fig"], use a generator expression inside max() with a key to find the longest word without a separate list.
Given numbers = range(1, 11), build one generator expression that filters even numbers, then feed it into another generator expression that doubles each — a simple two-stage pipeline.
Write a generator function countdown(n) that yields from n down to 1, then convert its output to a list using list().
Build a class Counter implementing the iterator protocol (__iter__ returning self, __next__ counting up and raising StopIteration past a limit).
Write two versions of a function that produces the numbers 1 to 5: one using return (builds and returns a full list), one using yield (a generator). Compare their type().
Write a generator function positive_only(numbers) that yields only the positive numbers from a given list.
Write a generator function name_and_greeting(name) that first yields the name, then yields a greeting built from it — showing a generator can yield DIFFERENT kinds of values in sequence.
Write a fibonacci(n) generator that yields the first n Fibonacci numbers lazily, one at a time.
Write an even_numbers_up_to(n) generator, then use it directly inside sum() to compute the total of all even numbers up to n without building a list.
Write a read_lines_lazily(filename) generator that opens a file and yields one stripped line at a time (tying back to the File Handling module).
Write a batches(items, size) generator that yields successive chunks (as lists) of a given size from a longer list.
Write an infinite natural_numbers() generator, then combine it with itertools.islice() to grab just the first 5 values.
Write a primes() generator that yields prime numbers indefinitely, testing each candidate as it goes.
Add a print("generating...") statement inside a generator function, then show that it doesn't print when the generator is CREATED — only when its values are actually consumed.
Write a words_from_text(text) generator that yields one word at a time from a sentence, using .split() internally.
The following code prints a generator object instead of the numbers 1-3. Find and fix the bug.
def count_to_three():
yield 1
yield 2
yield 3
print(count_to_three())Write a generator formatted_prices(prices) that yields each price formatted as currency ("$X.XX"), then print each yielded string.
Build a Countdown class implementing the full iterator protocol, counting down from n to 1, and demonstrate it in both a for loop and manual next() calls.
Write a generator all_numbers() that delegates to two smaller generators, first_half() and second_half(), using yield from for each.
Given an infinite count_up_from(start) generator, use itertools.islice(generator, start, stop) to grab a SLICE from the middle of an infinite sequence (not just the first N).
Using sys.getsizeof(), compare the memory footprint of range(1000000) versus list(range(1000000)), demonstrating why lazy sequences scale better.
Create a generator, iterate over it fully once with a for loop, then try to iterate over the SAME generator object again, showing it yields nothing the second time.
Write a recursive generator flatten(nested) that yields every element from an arbitrarily nested list, using yield from for the recursive case.
Build a MyRange class that fully replicates the behavior of the built-in range(start, stop, step), implementing __iter__/__next__.
Build a 3-stage generator pipeline over numbers = range(1, 21): filter multiples of 3, square each, then keep only results under 200 — all lazily chained.
Prove that a generator expression evaluates its condition LAZILY by combining it with a function that has a visible side effect (a print), and showing the prints are interleaved with consumption, not all upfront.
The following code is supposed to sum values from TWO separate uses of the same generator, but the second sum is always 0. Find and fix the bug.
def numbers():
yield 1
yield 2
yield 3
gen = numbers()
total1 = sum(gen)
total2 = sum(gen)
print(total1, total2)Build a "Fibonacci Generator Tool". Ask the user how many terms they want, then print them lazily using a generator (never storing the full sequence in a list).
Build a "Log Line Filter". Write a generator that reads a log file lazily and yields only lines containing "ERROR".
Build a "Batch Processor". Given a large list of records, write a generator yielding fixed-size batches, and process (print) each batch as if sending it to an API one chunk at a time.
Build a fully-featured custom EvenRange iterator class that yields only even numbers between a start and stop, implementing the complete iterator protocol.
Build a "Prime Finder Tool". Ask the user how many primes they want, and use an infinite primes() generator combined with itertools.islice() to get exactly that many.
Explain the precise difference between an "iterable" and an "iterator": every iterator is iterable, but not every iterable is an iterator. Demonstrate with a list.
Explain why generators are the right choice for processing huge datasets (e.g. a 10GB log file), tying it back to the lazy-evaluation and memory-footprint concepts from earlier in this module.
Demonstrate a REAL bug caused by generator exhaustion: a function that accepts a generator and iterates over it TWICE (once to count, once to process), silently processing nothing the second time. Fix it by converting to a list first.
Explain a practical use case for yield from: delegating work to sub-generators when flattening a tree-like structure, and why it's cleaner than manually looping and re-yielding.
Build an "Iterator & Generator Mastery Report" — the most complete demonstration in this module. Combine: a custom iterator class, a generator function using yield, a chained generator expression pipeline, and itertools.islice on an infinite generator — printing labeled results from each.
Still stuck on something?
Book a free 1-on-1 session and we'll work through it together.
Book a Free Session