A generator is a function that uses yield to produce a lazy sequence of values one at a time, without building the whole list in memory. It's ideal for streaming large test datasets.
Calling a generator function returns a generator object; each next() runs until the next yield, producing a value and pausing (retaining local state). This lazy evaluation means you can iterate over millions of rows or an infinite sequence without loading them all into memory. Generator expressions ((x for x in it)) are the compact form. Testers use generators to stream large data files, paginate API results, or feed parametrized tests without huge memory use. yield from delegates to a sub-generator.
Streaming a huge CSV so a load test doesn't exhaust memory: def rows(path): with open(path) as f: for line in f: yield line.strip() — each line is processed on demand.
A script does data = [transform(x) for x in huge_file_lines()] and runs out of memory. How do generators fix it?