Generators & yield Interview Questions & Answers

5 questions Updated 2026-06-18

Python interview questions on generators and yield, lazy evaluation, generator expressions vs list comprehensions, yield from, and infinite generators.

Read the in-depth guidePython Generators and yield Explained — Lazy Iteration and Memory

A generator is a function that produces a lazy sequence of values one at a time. Any function containing yield becomes a generator function: calling it doesn't run the body — it returns a generator object (an iterator). Each time you call next() (or iterate), the body runs until the next yield, hands back that value, and pauses, preserving all local state.

def counter():
    print("start")
    yield 1
    yield 2          # execution pauses here between next() calls
    yield 3

g = counter()        # nothing printed yet — body hasn't run
next(g)              # prints "start", returns 1
next(g)              # returns 2 (resumes after first yield)

When the function returns (or falls off the end), a StopIteration is raised to signal exhaustion. Generators are the simplest way to write a custom iterator without manually implementing __iter__/__next__.

A list materializes every element in memory at once, so its footprint grows with the number of items. A generator holds only its current state and computes each value on demand, so its memory use is roughly constant regardless of how many values it ultimately yields.

import sys
nums = [n * n for n in range(1_000_000)]   # ~8 MB list, built eagerly
gen  = (n * n for n in range(1_000_000))   # lazy — tiny, fixed size

sys.getsizeof(nums)   # large
sys.getsizeof(gen)    # ~100 bytes, regardless of range

This makes generators ideal for large or streaming data — reading a multi-gigabyte file line by line, or a pipeline of transformations — where you'd never want the whole dataset in RAM. The trade-off: you can only iterate a generator once, and you can't index or len() it.

They share syntax but differ in their brackets and behaviour. A list comprehension uses [...] and builds the entire list eagerly. A generator expression uses (...) and produces a lazy iterator that yields values one at a time, computing nothing until consumed.

lc = [x * 2 for x in range(5)]   # [0, 2, 4, 6, 8] — built now
ge = (x * 2 for x in range(5))   # <generator object> — built on demand

# parentheses are optional when it's the sole argument:
total = sum(x * 2 for x in range(5))   # streams — no temp list

Prefer a generator expression when feeding an aggregator like sum, max, any, or join over a large source — it avoids creating a throwaway list. Use a list comprehension when you need the full result repeatedly, want to index it, or need len().

yield from <iterable> delegates to a sub-iterator: it yields every value from the iterable as if you'd written a loop of yields, but also transparently forwards send, throw, and the sub-generator's return value. It's the clean way to compose or flatten generators.

def chain(*iterables):
    for it in iterables:
        yield from it          # vs: for x in it: yield x

list(chain([1, 2], (3, 4)))    # [1, 2, 3, 4]

def sub():
    yield 1
    return 99                  # captured by the delegator
def main():
    result = yield from sub()  # result == 99

Beyond saving a loop, yield from is what makes generator delegation and coroutine composition possible. Rule of thumb: use it whenever you want one generator to fully drain another.

Because generators are lazy, the body only advances when a value is requested — so an unbounded loop is fine: it never tries to produce all values at once. You control termination from the consumer side, by stopping iteration whenever you've taken enough.

def naturals():
    n = 0
    while True:        # infinite — but harmless
        yield n
        n += 1

from itertools import islice
list(islice(naturals(), 5))   # [0, 1, 2, 3, 4] — take just 5

This underpins itertools.count, cycle, and repeat, and lets you model streams elegantly. The danger is forgetting to bound the consumer: list(naturals()) or for x in naturals(): print(x) will run forever — pair infinite generators with islice, a break, or takewhile.

Practice tests are coming soon

Get notified when interactive mock interviews and quizzes launch.