What are Generators in Python?
A generator in Python is a special type of iterable that generates values on the fly instead of storing them in memory. It is created using functions with the yield
keyword, making them more memory-efficient than lists.
Why Use Python Generators?
✅ Memory Efficiency – Saves memory by generating values when needed
✅ Faster Execution – Avoids unnecessary computations for unused values
✅ Useful for Large Datasets – Handles infinite or large sequences efficiently
Difference Between Generators and Regular Functions
Feature | Regular Function | Generator Function |
---|---|---|
Returns | return statement | yield statement |
Memory Usage | Stores all values in memory | Generates values one by one |
Execution | Runs once and exits | Can pause and resume |
Iteration | Cannot be resumed | Can continue from the last yield |
Basic Syntax of a Generator
A generator function uses the yield
keyword to return values one at a time, instead of returning them all at once.
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
💡 Best Practice: Always use yield
instead of return
when dealing with large sequences.
Step-by-Step Guide with Examples
1. Creating a Simple Generator
✅ Example: Generator That Yields Numbers
def number_generator():
for i in range(1, 4):
yield i
gen = number_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
💡 Best Practice: Use yield
inside loops for better readability and efficiency.
2. Using Generators in a Loop
Instead of calling next()
, you can use a for
loop to iterate through a generator.
def count_down(n):
while n > 0:
yield n
n -= 1
for num in count_down(5):
print(num) # Output: 5, 4, 3, 2, 1
💡 Best Practice: Use for
loops with generators to automatically handle iteration.
3. Infinite Generators
Generators can be used to generate infinite sequences without using excessive memory.
✅ Example: Infinite Fibonacci Sequence
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
for _ in range(10):
print(next(fib), end=" ") # Output: 0 1 1 2 3 5 8 13 21 34
💡 Best Practice: Use infinite generators when streaming large data sets.
4. Generator Expressions
Similar to list comprehensions, generator expressions provide a compact way to create generators.
✅ Example: Generate Squares of Numbers
squares = (x**2 for x in range(5))
print(next(squares)) # Output: 0
print(next(squares)) # Output: 1
print(list(squares)) # Output: [4, 9, 16]
💡 Best Practice: Use generator expressions when you need a lightweight alternative to list comprehensions.
5. Chaining Generators
Generators can be combined to process data in steps.
✅ Example: Filter Even Numbers from a Generator
def numbers():
for i in range(10):
yield i
def even_numbers(gen):
for num in gen:
if num % 2 == 0:
yield num
evens = even_numbers(numbers())
print(list(evens)) # Output: [0, 2, 4, 6, 8]
💡 Best Practice: Chain multiple generators for complex data pipelines.
6. Generator vs List – Memory Comparison
✅ Example: Memory Efficiency of Generators vs Lists
import sys
# List storing 1 million numbers
num_list = [x for x in range(1_000_000)]
print(sys.getsizeof(num_list), "bytes") # Large memory usage
# Generator storing 1 million numbers
num_gen = (x for x in range(1_000_000))
print(sys.getsizeof(num_gen), "bytes") # Much smaller memory usage
💡 Best Practice: Use generators instead of lists for large data processing.
When to Use Generators?
✔ Large Datasets – Processing big files, logs, or databases
✔ Streaming Data – APIs, real-time data processing
✔ Memory Optimization – Generating values one at a time
🚫 Avoid generators when you need random access to elements since they do not support indexing.