A Python generator is an iterator that produces values lazily. Since it doesn’t store all values in memory at once, you need to consume it to visualize its contents. Here are some ways to visualize a generator:
def my_generator():
for i in range(5):
yield i**2
gen = my_generator()
print(list(gen)) # Convert to a list for visualization
Output:
[0, 1, 4, 9, 16]
Note: Once a generator is consumed, it cannot be reused.
the range function returns a generator object.
x = range(5)
print(x)
print(type(x))
results in:
range(0, 5)
<class 'range'>
so you can see that ,while we can visualise the range function as returning something like a list, it is not a list but a generator!
A generator in Python is an iterator that produces values on demand and does not store them in memory. It generates each value when requested and forgets it once it moves to the next value. This makes generators efficient for working with large or infinite sequences.
However, once a generator is exhausted, it cannot be reset or reused without recreating it.
def my_generator():
for i in range(3):
yield i
gen = my_generator()
print(list(gen)) # Consumes the generator completely
print(list(gen)) # No output, because it's already exhausted
Output:
[0, 1, 2]
[]
Here, the second print(list(gen)) gives an empty list because the generator has already been exhausted.
Each generator maintains an internal state, which tracks where it is in its iteration process. When all values are yielded, the generator reaches a "StopIteration" state, meaning it is done.
Once a generator is exhausted, calling it again will not restart it.
If you need to iterate multiple times, you have a few options:
1. Create a New Generator Instance
gen1 = my_generator() # First generator
print(list(gen1)) # Consume it
gen2 = my_generator() # Create a new one
print(list(gen2)) # Works again
2. Convert to a List (If Memory Allows)
values = list(my_generator()) # Store values in a list
print(values) # Use multiple times
print(values) # Still works
This is useful if the generator produces a small, finite number of values.
3. Use itertools.tee() to Create Multiple Iterators
If you need to iterate over the same generator multiple times without storing all values upfront, use itertools.tee():
from itertools import tee
gen = my_generator()
gen1, gen2 = tee(gen) # Creates two independent iterators
print(list(gen1)) # Works
print(list(gen2)) # Also works
⚠️ Warning: tee() buffers values in memory, so it may not be ideal for very large generators.
If you pass a generator to a function that consumes it, you cannot use it again afterward.
If you need multiple passes over the data, use list(), tee(), or recreate the generator.
If you're working with large data, avoid list() and prefer re-creating the generator.