A generator produces one value at a time, pausing until the next value is requested. Because it does not store every result together, it can reduce memory use when processing large sequences. section 4.2.3
The yield keyword returns a value and pauses execution, allowing the generator to continue preparing the next value afterward. A typical generator sets up its state, loops, yields each result, and updates that state. section 4.2.3
Squared values
def squares(items): for item in items: yield item ** 2 This produces squared values as they are requested instead of constructing a complete list first. section 4.2.3











