Introduction
You explain that a generator produces values one at a time. Then the interviewer asks what happens when you convert the same generator to a list twice. Does it repeat the values, return an empty list or raise an error?
Answering confidently requires understanding what has already been consumed. These Python generators and iterators interview questions connect definitions to code behaviour, practical choices and common mistakes.
You should be comfortable with loops, functions and lists. If those still need work, start with these Python practice problems before tackling iterator state and lazy evaluation. For each example, predict the result before running it.
Example validation: The Python code blocks were executed in Python 3.12.14. Displayed results and the deliberately raised exception were checked. Run each example separately; the file example requires the supplied sample input.
Understand the Iteration Protocol
1. What is the difference between an iterable, an iterator and a generator?
An iterable can supply an iterator. An iterator produces successive items and tracks its progress. A generator object is a particular kind of iterator, created by a generator function or generator expression.
| Concept | Example | Key behaviour |
| Iterable | [10, 20] | A list can supply a fresh iterator for another pass |
| Iterator | iter([10, 20]) | Each next() advances its position |
| Generator object | (n * 2 for n in range(3)) | Produces results as iteration requests them |
Every generator object is an iterator, and every iterator is iterable. However, not every iterable is an iterator: a list does not itself support next().
Also distinguish the function from its result. Calling a generator function creates the generator object you iterate over.
2. How does a for loop use iter() and next()?
A for loop obtains an iterator with iter(), repeatedly requests its next item and ends when StopIteration signals exhaustion.
You can inspect that progression manually:
items = iter([4, 8])
print(next(items)) # 4
print(next(items)) # 8
print(next(items, "done")) # done
Without the default argument, the final call would raise StopIteration. The default handles exhaustion; it does not suppress unrelated errors.
3. How do you create a custom iterator?
Implement __iter__() to return the iterator itself and __next__() to produce a value or raise StopIteration.
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
value = self.current
self.current -= 1
return value
print(list(Countdown(3))) # [3, 2, 1]
This example assumes an integer starting value. Zero or a negative value produces no items. A class is useful when iteration belongs within an object with other behaviour; a generator can express a simple sequence more compactly.
Explain Generator Behaviour
4. How does yield differ from return?
yield produces a value and suspends execution, preserving local state. Resuming the generator continues after that suspension point. In a generator, return ends execution rather than yielding a final item.
def steps():
print("started")
yield 10
yield 20
return
values = steps()
print("created")
print(next(values))
print(next(values))
print(next(values, "done"))
Output:
created
started
10
20
done
Calling steps() does not execute its body. The first next() starts it. This timing matters when the body performs expensive work or could raise an exception.
5. How does a generator expression differ from a list comprehension?
A list comprehension builds its result list immediately. A generator expression calculates successive results as they are requested.
squares_list = [n * n for n in range(3)]
squares_generator = (n * n for n in range(3))
print(squares_list) # [0, 1, 4]
print(list(squares_generator)) # [0, 1, 4]
The list supports indexing and repeated traversal. The generator provides a forward-only stream without those list operations.
One detail worth explaining: the leftmost iterable expression in a generator expression is evaluated immediately. Laziness does not mean that every operation waits until the first next().
6. Can you reuse a generator after consuming it?
An exhausted generator cannot be restarted. Partial consumption also changes what remains available:
squares = (n * n for n in range(2, 5))
print(next(squares)) # 4
print(list(squares)) # [9, 16]
print(list(squares)) # []
The first call consumes one value. The first list() consumes the rest. The second finds nothing remaining.
Create a new generator to repeat the calculation, or store the results if repeated access is required. Calling iter() on the existing generator returns that same object and does not reset it.
7. Why can raising StopIteration inside a generator cause RuntimeError?
In modern Python, a StopIteration exception that escapes a generator's body is converted into RuntimeError.
def broken():
yield 1
raise StopIteration
list(broken()) # Raises RuntimeError
Replace raise StopIteration with return to finish this generator normally. Reaching the end of the function also works.
This differs from the custom iterator above, where raising StopIteration from __next__() is the correct end signal. The distinction prevents an unexpected exhaustion error inside generator code from silently truncating its output.
Apply Generators to Data Problems
CompeteX is PangaeaX's platform for practical data and technical competitions. When tackling Python coding challenges, identifying whether a task needs one pass or repeated access is a useful part of explaining your approach. The next questions practise that decision-making.
8. What does yield from do?
yield from delegates iteration to another iterable. Here it produces the individual values from each batch:
def flatten_batches(batches):
for batch in batches:
yield from batch
print(list(flatten_batches([[2, 4], [], [6]])))
# [2, 4, 6]
This flattens one level, not arbitrarily nested data. For this example, a nested loop with yield would produce the same values. More advanced delegation can also pass generator-control operations and capture a subgenerator's return value.
9. Are generators always faster or more memory-efficient?
No. A generator can avoid storing every output at once, which helps when processing a large stream. It can still retain input objects and local state, and converting its output to a list creates that list in memory.
A custom iterator can also produce values lazily. Laziness is not exclusive to generators.
Choose based on the task:
- One-pass aggregation: A generator can feed values directly into sum().
- Repeated traversal or indexing: Keeping a list may be appropriate.
- Speed-sensitive processing: Measure the complete workload rather than assuming a generator is faster.
10. How would you process a large file with a generator?
Iterate over the file and transform values as the consumer requests them. This example expects one integer quantity per line, ignores blank lines and yields only positive quantities. Invalid non-integer text raises ValueError.
Create quantities.txt containing:
10
-2
0
7
Then run:
def positive_quantities(lines):
for line in lines:
if not line.strip():
continue
quantity = int(line)
if quantity > 0:
yield quantity
with open("quantities.txt", encoding="utf-8") as source:
total = sum(positive_quantities(source))
print(total) # 17
The code avoids loading the entire file or building a list of filtered quantities. Consumption happens inside the with block, while the file is open. An empty file produces a total of zero.
Practise Explaining Your Code
A useful interview answer explains what the code produces, when it runs, what consumes its values and how it handles empty or invalid input. Practise changing one input and predicting the result before checking it.
If you want to demonstrate that understanding beyond interview preparation, AuthenX is PangaeaX's skill authentication platform using AI-led interviews and portfolio screening. Explaining your technical choices clearly can support the evidence provided by your projects and practical work.
Return to the opening question: converting the same exhausted generator to a list again produces an empty list. Being able to explain why, and when to create a fresh generator or keep a list instead, makes the answer useful beyond the interview.

