What are generators and iterators, and when would you use yield instead of returning a list?
An iterator is any object with __iter__ and __next__, producing values one at a time until it raises StopIteration. A generator is the easy way to write one: a function containing yield. Calling it does not run the body — it returns a generator object, and each next() runs until the next yield, then suspends with its local state intact.
def read_large_file(path):
with open(path) as f:
for line in f:
yield line.strip()Why use one instead of building a list:
- Memory. A list of ten million rows needs all ten million in RAM. A generator holds one at a time — this is the whole argument.
- Laziness. Work is done only as consumed, so an early
breakcosts nothing. - Composition. Generators chain into pipelines where each stage streams into the next.
- Infinite sequences become expressible at all.
The trade-offs: you can only iterate once, and you cannot index or take len(). If you need random access or multiple passes, build the list.
Note: A generator expression is the same thing in one line — sum(x*x for x in data) never materialises the intermediate sequence, unlike the list-comprehension version.





