Examining consumed generators reveals a key property of Python generators: once partially or fully consumed (via next(), for loop, list(), sum(), etc.), they cannot be reset or reused — they are exhausted and yield no more values. However, you can still inspect what remains by converting the remaining generator to a list (or other structure) with list(gen), tuple(gen), or list(islice(gen, n)) — this captures any unconsumed elements without side effects. In 2026, understanding generator exhaustion is critical for debugging pipelines, lazy evaluation in pandas/Polars, streaming data processing, and avoiding subtle bugs where a generator appears “empty” after earlier consumption. Always create fresh generators when reuse is needed — or collect results eagerly if inspection/reuse is required.
Here’s a complete, practical guide to examining consumed generators in Python: exhaustion mechanics, inspecting remainders, resetting/recreating, real-world patterns, and modern best practices with type hints, slicing, Polars lazy equivalents, and memory considerations.
Generator exhaustion basics — consuming part/all leaves nothing for later; StopIteration signals end.
def generate_numbers():
for num in range(1, 11):
yield num
numbers_gen = generate_numbers()
# Consume first 5
for _ in range(5):
next(numbers_gen)
# Examine remaining (6–10)
remaining = list(numbers_gen)
print(remaining) # [6, 7, 8, 9, 10]
# Now exhausted — no more values
print(list(numbers_gen)) # [] (empty)
next(numbers_gen) # StopIteration
Inspecting partial consumption — use list(), tuple(), or itertools.islice to capture remainder without full consumption.
from itertools import islice
gen = (x**2 for x in range(20)) # 0² to 19²
# Consume first 5
for _ in range(5):
next(gen)
# Peek at next 3 without consuming
peek = list(islice(gen, 3)) # [25, 36, 49] (6², 7², 8²)
print(peek)
# Now consume those
for _ in range(3):
next(gen)
# Remaining after peek/consume
print(list(gen)) # [81, 100, ..., 361]
Real-world pattern: debugging chunked pandas/Polars CSV processing — examine consumed generator of chunks to verify filtering/aggregation.
def csv_chunks(file_path: str, chunksize: int = 100_000):
"""Generator: yield DataFrame chunks."""
for chunk in pd.read_csv(file_path, chunksize=chunksize):
yield chunk
chunks_gen = csv_chunks('large.csv')
# Consume first chunk, filter, inspect remainder
first_chunk = next(chunks_gen)
filtered_first = first_chunk[first_chunk['value'] > 100]
# Examine remaining chunks (without loading all)
remaining_chunks = list(islice(chunks_gen, 2)) # peek next 2 chunks
for i, chunk in enumerate(remaining_chunks):
print(f"Remaining chunk {i+1} shape: {chunk.shape}")
print(chunk.head(3))
# Now continue consuming full generator if needed
Best practices make consumed generator examination safe, efficient, and debug-friendly. Understand exhaustion — generators are single-pass; recreate with new call (gen = gen_func()) for reuse. Modern tip: use Polars lazy scanning — pl.scan_csv(...).collect() or .fetch(n_rows=100_000) for partial inspection without full load. Use itertools.islice(gen, n) — peek limited items without consuming all. Convert to list only when needed — list(gen) exhausts and stores remainder. Use tee() — itertools.tee(gen, n) creates multiple independent iterators from one. Add type hints — def gen_func() -> Generator[pd.DataFrame, None, None]. Avoid list(gen) on huge generators — use islice or consume incrementally. Log consumption — print("Consumed chunk", i) inside loop for tracing. Handle StopIteration — next(gen, default) for safe single access. Use yield from — delegate to sub-generators cleanly. Test generator behavior — consume partially, assert remainder correct with list(gen). Use Polars .head(n) — fast preview of lazy frame without full collect. Combine with contextmanager — ensure cleanup even on partial consumption.
Examining consumed generators captures remaining elements after partial use — use list(), islice(), or tee() to inspect without full consumption. In 2026, recreate generators for reuse, prefer Polars lazy fetch() for previews, use type hints, and avoid large list(gen) conversions. Master consumed generator examination, and you’ll debug, verify, and explore lazy pipelines reliably and memory-efficiently.
Next time your generator seems empty — examine what’s left. It’s Python’s cleanest way to say: “Show me what remains after consumption.”