Iterator
Iterators and generators are Python's mechanism for lazy evaluation — producing values on demand rather than storing entire sequences in memory. They enable efficient processing of large or infinite data streams. The guide walks through Iterator Protocol, Generator Functions, Generator Expressions, itertools Module, Custom Iterators. An object is iterable if it implements `__iter__()` returning an iterator. An iterator implements `__next__()` returning the next item and raising `StopIteration` when exhausted. Use `iter()` to get an iterator and `next()` to manually advance it. Functions containing `yield` (instead of `return`) are generators. Each call to `next()` on a generator object resumes execution until the next `yield`. Generators maintain their local state between calls, making them ideal for streams and infinite sequences. Generator expressions `(x**2 for x in range(10))` are like list comprehensions but produce items lazily. They use round parentheses instead of square brackets. Use them for memory-efficient processing of large datasets without creating intermediate lists.