Generators in Python: A Complete Guide
I am a Tech Enthusiast having 13+ years of experience in ๐๐ as a ๐๐จ๐ง๐ฌ๐ฎ๐ฅ๐ญ๐๐ง๐ญ, ๐๐จ๐ซ๐ฉ๐จ๐ซ๐๐ญ๐ ๐๐ซ๐๐ข๐ง๐๐ซ, ๐๐๐ง๐ญ๐จ๐ซ, with 12+ years in training and mentoring in ๐๐จ๐๐ญ๐ฐ๐๐ซ๐ ๐๐ง๐ ๐ข๐ง๐๐๐ซ๐ข๐ง๐ , ๐๐๐ญ๐ ๐๐ง๐ ๐ข๐ง๐๐๐ซ๐ข๐ง๐ , ๐๐๐ฌ๐ญ ๐๐ฎ๐ญ๐จ๐ฆ๐๐ญ๐ข๐จ๐ง ๐๐ง๐ ๐๐๐ญ๐ ๐๐๐ข๐๐ง๐๐. I have ๐๐๐๐๐๐๐ ๐๐๐๐ ๐๐๐๐ 10,000+ ๐ฐ๐ป ๐ท๐๐๐๐๐๐๐๐๐๐๐๐ and ๐๐๐๐ ๐๐๐๐๐ ๐๐๐๐ ๐๐๐๐ 500+ ๐๐๐๐๐๐๐๐ ๐๐๐๐๐๐๐๐ in the areas of ๐๐จ๐๐ญ๐ฐ๐๐ซ๐ ๐๐๐ฏ๐๐ฅ๐จ๐ฉ๐ฆ๐๐ง๐ญ, ๐๐๐ญ๐ ๐๐ง๐ ๐ข๐ง๐๐๐ซ๐ข๐ง๐ , ๐๐ฅ๐จ๐ฎ๐, ๐๐๐ญ๐ ๐๐ง๐๐ฅ๐ฒ๐ฌ๐ข๐ฌ, ๐๐๐ญ๐ ๐๐ข๐ฌ๐ฎ๐๐ฅ๐ข๐ณ๐๐ญ๐ข๐จ๐ง๐ฌ, ๐๐ซ๐ญ๐ข๐๐ข๐๐ข๐๐ฅ ๐๐ง๐ญ๐๐ฅ๐ฅ๐ข๐ ๐๐ง๐๐ ๐๐ง๐ ๐๐๐๐ก๐ข๐ง๐ ๐๐๐๐ซ๐ง๐ข๐ง๐ . I am interested in ๐ฐ๐ซ๐ข๐ญ๐ข๐ง๐ ๐๐ฅ๐จ๐ ๐ฌ, ๐ฌ๐ก๐๐ซ๐ข๐ง๐ ๐ญ๐๐๐ก๐ง๐ข๐๐๐ฅ ๐ค๐ง๐จ๐ฐ๐ฅ๐๐๐ ๐, ๐ฌ๐จ๐ฅ๐ฏ๐ข๐ง๐ ๐ญ๐๐๐ก๐ง๐ข๐๐๐ฅ ๐ข๐ฌ๐ฌ๐ฎ๐๐ฌ, ๐ซ๐๐๐๐ข๐ง๐ ๐๐ง๐ ๐ฅ๐๐๐ซ๐ง๐ข๐ง๐ new subjects.
Introduction to Generators
Generators in Python are a special type of iterable that allow lazy evaluation, meaning they generate values one at a time instead of storing them all in memory at once. They are useful for handling large datasets, optimizing memory usage, and improving performance in scenarios where iterating over huge amounts of data is required.
Why Use Generators?
Improves Memory Efficiency: Unlike lists, generators do not store all values in memory.
Faster Execution: Values are generated as needed, reducing unnecessary processing.
Supports Infinite Sequences: You can define generators that run indefinitely.
Simplifies Complex Iteration Logic: Avoids using multiple temporary variables.
Creating a Basic Generator
A generator function uses the yield keyword to return values lazily.
Example 1: Simple Generator
def simple_generator():
yield 1
yield 2
yield 3
gen = simple_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
How It Works
The function
simple_generator()does not return all values at once.It pauses execution at each
yieldand resumes whennext()is called.Once all values are yielded, further
next()calls raiseStopIteration.
Iterating Over a Generator Using for Loop
A generator can be iterated over using a for loop:
def number_generator():
for i in range(5):
yield i
for num in number_generator():
print(num)
Output
0
1
2
3
4
This avoids calling next() manually and ensures graceful handling of StopIteration.
Generator Expressions
Generators can be created using generator expressions, similar to list comprehensions.
gen_exp = (x * x for x in range(5))
print(next(gen_exp)) # Output: 0
print(next(gen_exp)) # Output: 1
print(next(gen_exp)) # Output: 4
This is more memory-efficient than list comprehensions ([x * x for x in range(5)]), which store all values in memory.
Generators vs Lists (Memory Usage)
Consider generating 1 million numbers:
Using Lists (Consumes More Memory)
nums_list = [x * x for x in range(1000000)]
Using Generators (Efficient Memory Usage)
nums_gen = (x * x for x in range(1000000))
Lists store all values in memory upfront.
Generators compute values only when needed, reducing memory footprint.
Generators for Infinite Sequences
Generators can be used to create infinite sequences:
def infinite_counter():
num = 0
while True:
yield num
num += 1
counter = infinite_counter()
print(next(counter)) # Output: 0
print(next(counter)) # Output: 1
print(next(counter)) # Output: 2
This is ideal for streaming data, real-time processing, or dynamically generating content.
Using yield with send()
Generators can receive values using the send() method.
def greeter():
name = yield "Enter your name:"
yield f"Hello, {name}!"
g = greeter()
print(next(g)) # Output: Enter your name:
print(g.send("Rahul")) # Output: Hello, Rahul!
next()starts the generator.send()sends a value to theyieldexpression.
Using yield from (Delegating Iteration)
To yield values from another iterable, use yield from:
def sub_generator():
yield from [10, 20, 30]
for num in sub_generator():
print(num)
Output
10
20
30
This is cleaner than looping through another iterable manually.
Real-World Applications of Generators
1. Processing Large Files Without Loading into Memory
def read_large_file(file_path):
with open(file_path, "r") as file:
for line in file:
yield line
for line in read_large_file("data.txt"):
print(line)
- Processes huge files efficiently without reading the entire file at once.
2. Streaming Live Sensor Data
import random
import time
def sensor_stream():
while True:
yield random.uniform(20, 30) # Simulating temperature sensor
time.sleep(1) # Wait for next reading
sensor = sensor_stream()
for _ in range(5):
print(next(sensor))
- Continuously streams live data without storing previous values.
3. Generating Fibonacci Sequence
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib_gen = fibonacci()
for _ in range(10):
print(next(fib_gen))
- Generates Fibonacci numbers without storing previous values.


