Comprehensions & Generators
Concise, often faster ways to build sequences — and the lazy-evaluation alternative that avoids materializing them at all.
Try answering in your head first, then click a question to check the model answer.
Q1.When would you use a generator expression instead of a list comprehension?(show answer)
When you only need to iterate the values once and don't need random access or the full collection in memory at once — e.g. streaming through a huge file line by line, or feeding a pipeline where each stage processes one item at a time. A list comprehension is better when you need to reuse, index, or measure the length of the result.
Q2.How does `yield` change a function's execution model compared to a normal function with `return`?(show answer)
A function with yield becomes a generator function — calling it doesn't run the body, it returns a generator object. Each call to next() runs the body until the next yield, pauses execution there (preserving local state), and resumes exactly there on the next next() call, instead of running start-to-finish and returning once.
Q3.What's the difference between a list comprehension and a dict/set comprehension syntactically?(show answer)
A list comprehension uses square brackets: [x for x in iterable]. A set comprehension uses curly braces with a single expression: {x for x in iterable}. A dict comprehension also uses curly braces but with a key:value pair: {k: v for k, v in iterable}.
Q4.Why can a generator only be iterated once?(show answer)
A generator holds its execution state (where it paused) rather than a stored collection of values — once it runs to completion (reaches a return or falls off the end), that state is exhausted and there's nothing left to resume. To iterate again, you need a fresh generator object, e.g. by calling the generator function again.
