Python generators ki detailed guide — yield keyword, generator functions, generator expressions, send/throw/close, aur real-world use cases jaise infinite sequences, data pipelines, aur memory-efficient file processing. Hindi mein.
Generator ek special function hai jo values ek ek karke produce karta hai — sab kuch memory mein ek saath nahi rakhta. Yeh Python ka memory magic hai!
1️⃣ Pehla Generator — yield Keyword
# ─────────────────────────────────────
# Simple generator function
# ─────────────────────────────────────
def count_up(n):
"""1 se n tak count karta hai — lazily"""
print(f" [Generator start hua]")
for i in range(1, n + 1):
print(f" [Yield karne wala: {i}]")
yield i # Yahan PAUSE hota hai
print(f" [Resume hua after {i}]")
print(f" [Generator khatam hua]")
# Generator object banao (abhi kuch execute nahi hua!)
gen = count_up(3)
print(f"Type: {type(gen)}") # generator
print(f"Object: {gen}")
print()
# next() se ek ek value lo
print(f"First : {next(gen)}")
print()
print(f"Second : {next(gen)}")
print()
print(f"Third : {next(gen)}")
print()
# Aur next() call karein → StopIteration
try:
print(f"Fourth : {next(gen)}")
except StopIteration:
print("⛔ StopIteration — generator exhausted!")
| Type | <class 'generator'> |
| Object | <generator object count_up at 0x...> |
| [Yield karne wala | 1] |
| First | 1 |
| [Yield karne wala | 2] |
| Second | 2 |
| [Yield karne wala | 3] |
| Third | 3 |
2️⃣ Generator in for Loop
# ─────────────────────────────────────
# for loop automatically next() call karta hai
# ─────────────────────────────────────
def squares(n):
"""Pehle n numbers ke squares yield karta hai"""
for i in range(1, n + 1):
yield i ** 2
# for loop ke saath
print("Squares up to 10:")
for sq in squares(10):
print(sq, end=" ")
print()
# list() se sab convert karo
all_squares = list(squares(5))
print(f"\nList form: {all_squares}")
# sum(), max(), min() direct use
print(f"Sum of squares 1-10: {sum(squares(10))}")
print(f"Max of squares 1-5 : {max(squares(5))}")
| List form | [1, 4, 9, 25] |
| Sum of squares 1-10 | 385 |
| Max of squares 1-5 | 25 |
3️⃣ Infinite Generator — Kabhi Na Ruke!
# ─────────────────────────────────────
# Infinite generators — no end!
# ─────────────────────────────────────
def natural_numbers():
"""1, 2, 3, 4, ... forever"""
n = 1
while True: # Infinite loop — intentional!
yield n
n += 1
def fibonacci():
"""0, 1, 1, 2, 3, 5, 8, ... forever"""
a, b = 0, 1
while True:
yield a
a, b = b, a + b
def cycle(items):
"""Items ko infinite loop mein cycle karta hai"""
while True:
for item in items:
yield item
# Take only N items from infinite generator
def take(n, gen):
"""Generator se first n items lo"""
for _ in range(n):
yield next(gen)
# Natural numbers
nats = natural_numbers()
print("First 10 natural numbers:")
print(list(take(10, nats)))
# Fibonacci
fibs = fibonacci()
print("\nFirst 15 Fibonacci numbers:")
print(list(take(15, fibs)))
# Cycle
days = cycle(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"])
print("\nNext 10 days (starting from Mon):")
print(list(take(10, days)))
# OUTPUT:
First 10 natural numbers:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
First 15 Fibonacci numbers:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
Next 10 days (starting from Mon):
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Mon', 'Tue', 'Wed']
4️⃣ Generator Expressions (Compact Syntax)
# ─────────────────────────────────────
# Generator expression — list comprehension jaisi
# [] ki jagah () use karo
# ─────────────────────────────────────
# List comprehension — sab memory mein
squares_list = [x**2 for x in range(1, 11)]
print(f"List type: {type(squares_list)}")
print(f"List: {squares_list}")
# Generator expression — lazy
squares_gen = (x**2 for x in range(1, 11))
print(f"\nGen type: {type(squares_gen)}")
print(f"Gen object: {squares_gen}")
# Use karo
print(f"Values: {list(squares_gen)}")
# Memory comparison
import sys
large_list = [x for x in range(1_000_000)]
large_gen = (x for x in range(1_000_000))
print(f"\nMemory comparison (1 million items):")
print(f" List size : {sys.getsizeof(large_list):>12,} bytes ({sys.getsizeof(large_list)//1024//1024} MB)")
print(f" Gen size : {sys.getsizeof(large_gen):>12,} bytes (practically nothing!)")
print(f" Savings : {sys.getsizeof(large_list) / sys.getsizeof(large_gen):.0f}x less memory!")
| List type | <class 'list'> |
| List | [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] |
| Gen type | <class 'generator'> |
| Gen object | <generator object <genexpr> at 0x...> |
| Values | [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] |
| List size | 8,448,728 bytes (8 MB) |
| Gen size | 128 bytes (practically nothing!) |
| Savings | 66006x less memory! |
5️⃣ yield from — Generator Delegation
# ─────────────────────────────────────
# yield from — doosre generator se delegate karna
# ─────────────────────────────────────
def gen_1_to_5():
yield from range(1, 6)
def gen_a_to_e():
yield from "ABCDE"
def combined():
"""Multiple generators ko chain karna"""
yield from gen_1_to_5()
yield "---"
yield from gen_a_to_e()
yield "END"
print("Combined generator:")
for item in combined():
print(item, end=" ")
print()
# ─── Nested list flatten with yield from ───
def flatten(nested):
"""Nested structure ko recursively flatten karta hai"""
for item in nested:
if isinstance(item, (list, tuple)):
yield from flatten(item) # Recursive delegation
else:
yield item
nested_data = [1, [2, 3, [4, 5]], [6, [7, [8, 9]]], 10]
print(f"\nNested : {nested_data}")
print(f"Flat : {list(flatten(nested_data))}")
# OUTPUT:
Combined generator:
1 2 3 4 5 --- A B C D E END
Nested : [1, [2, 3, [4, 5]], [6, [7, [8, 9]]], 10]
Flat : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
6️⃣ Generator .send() — Two-Way Communication
# ─────────────────────────────────────
# .send() — generator ko value bhejo
# Generator coroutine ki tarah kaam karta hai
# ─────────────────────────────────────
def accumulator():
"""
Running total maintain karta hai.
.send() se values bhejo, yield se current total wapas aata hai.
"""
total = 0
while True:
value = yield total # yield: total deta hai, value: receive karta hai
if value is None:
break
total += value
# Coroutine setup
acc = accumulator()
next(acc) # "Prime" the generator (pehla yield tak pahuncho)
# Values bhejo
print(acc.send(100)) # 100
print(acc.send(250)) # 350
print(acc.send(75)) # 425
print(acc.send(500)) # 925
print(f"Final total: {acc.send(0)}") # 925
# OUTPUT:
100
350
425
925
Final total: 925
7️⃣ Real-World: Large File Processing
# ─────────────────────────────────────
# 📂 Large file ko chunks mein process karna
# Generator se memory efficient hai
# ─────────────────────────────────────
def read_csv_rows(filename, skip_header=True):
"""
CSV file ke rows ek ek karke yield karta hai.
Poori file ek saath load nahi karta.
"""
try:
with open(filename, 'r', encoding='utf-8') as f:
if skip_header:
next(f) # Header skip karo
for line_num, line in enumerate(f, 1):
row = line.strip().split(',')
yield line_num, row
except FileNotFoundError:
print(f"File not found: {filename}")
return
def read_in_chunks(iterable, chunk_size=1000):
"""
Iterable ko fixed-size chunks mein yield karta hai.
Batch processing ke liye useful.
"""
chunk = []
for item in iterable:
chunk.append(item)
if len(chunk) >= chunk_size:
yield chunk
chunk = []
if chunk: # Remaining items
yield chunk
# Simulated large log file processing
def parse_log_lines(log_text):
"""Log lines ko parse karke meaningful data yield karta hai"""
for line in log_text.strip().split('\n'):
parts = line.strip().split(' | ')
if len(parts) >= 3:
yield {
"timestamp": parts[0],
"level": parts[1].strip(),
"message": parts[2].strip()
}
# Sample log data (normally yeh huge file hoga)
sample_logs = """2026-06-12 10:00:01 | INFO | Server started
2026-06-12 10:00:02 | INFO | Database connected
2026-06-12 10:01:15 | ERROR | Connection timeout
2026-06-12 10:01:16 | WARN | Retrying connection
2026-06-12 10:01:18 | INFO | Connection restored
2026-06-12 10:05:22 | ERROR | Disk space low (85%)
2026-06-12 10:10:00 | INFO | Backup started
2026-06-12 10:15:30 | INFO | Backup completed"""
# Process only ERROR logs
error_logs = (log for log in parse_log_lines(sample_logs) if log["level"] == "ERROR")
print("🔴 ERROR Logs:")
for log in error_logs:
print(f" [{log['timestamp']}] {log['message']}")
# Count by level
level_counts = {}
for log in parse_log_lines(sample_logs):
level = log["level"].strip()
level_counts[level] = level_counts.get(level, 0) + 1
print(f"\n📊 Log Statistics:")
for level, count in sorted(level_counts.items()):
bar = "█" * count
print(f" {level:<6}: {bar} ({count})")
| [2026-06-12 10 | 01:15] Connection timeout |
| [2026-06-12 10 | 05:22] Disk space low (85%) |
| ERROR | ██ (2) |
| INFO | █████ (5) |
| WARN | █ (1) |
8️⃣ Real-World: Data Pipeline with Generators
# ─────────────────────────────────────
# 🔄 Data Pipeline — generators chain karo
# ─────────────────────────────────────
# Step 1: Data source
def data_source(records):
"""Raw data produce karta hai"""
print("📥 Data source starting...")
for record in records:
yield record
# Step 2: Filter
def filter_active(records):
"""Sirf active records pass karo"""
for record in records:
if record.get("active", False):
yield record
# Step 3: Transform
def transform(records):
"""Data format change karo"""
for record in records:
yield {
"id": record["id"],
"full_name": f"{record['first']} {record['last']}",
"email": record.get("email", "N/A").lower(),
"department": record.get("dept", "Unknown").upper(),
}
# Step 4: Enrich
def add_employee_id(records, prefix="EMP"):
"""Employee ID add karo"""
for i, record in enumerate(records, 1):
record["emp_id"] = f"{prefix}-{i:04d}"
yield record
# Raw data
raw_data = [
{"id": 1, "first": "Rahul", "last": "Sharma", "email": "RAHUL@Co.com", "dept": "engineering", "active": True},
{"id": 2, "first": "Priya", "last": "Gupta", "email": "priya@co.com", "dept": "marketing", "active": False},
{"id": 3, "first": "Amit", "last": "Kumar", "email": "AMIT@Co.com", "dept": "engineering", "active": True},
{"id": 4, "first": "Seema", "last": "Joshi", "email": "seema@co.com", "dept": "hr", "active": True},
{"id": 5, "first": "Vijay", "last": "Patel", "email": "VIJAY@Co.com", "dept": "marketing", "active": False},
]
# Pipeline banana — generators chain karo
pipeline = add_employee_id(
transform(
filter_active(
data_source(raw_data)
)
)
)
# Process karo — LAZILY!
print("👥 Active Employees After Pipeline:")
print("=" * 60)
for employee in pipeline:
print(f" {employee['emp_id']}: {employee['full_name']:<20} | {employee['email']:<25} | {employee['department']}")
| EMP-0001 | Rahul Sharma | rahul@co.com | ENGINEERING |
| EMP-0002 | Amit Kumar | amit@co.com | ENGINEERING |
| EMP-0003 | Seema Joshi | seema@co.com | HR |
# ─────────────────────────────────────
# 📄 Pagination — API results paginate karna
# ─────────────────────────────────────
def paginate(data, page_size=3):
"""
Data ko pages mein yield karta hai.
API pagination simulate karta hai.
"""
total = len(data)
total_pages = (total + page_size - 1) // page_size
for page_num in range(1, total_pages + 1):
start = (page_num - 1) * page_size
end = min(start + page_size, total)
page_data = data[start:end]
yield {
"page": page_num,
"total_pages": total_pages,
"total_items": total,
"items_on_page": len(page_data),
"data": page_data
}
# Sample product catalog
products = [
{"id": i, "name": f"Product {i}", "price": 100 * i}
for i in range(1, 22) # 21 products
]
# Paginate with 5 per page
print("🛒 Product Catalog (5 per page)")
print("=" * 50)
pages = paginate(products, page_size=5)
# Only first 3 pages dekhte hain (lazy!)
for page_info in pages:
print(f"\n📄 Page {page_info['page']}/{page_info['total_pages']}"
f" (showing {page_info['items_on_page']} of {page_info['total_items']} items)")
print("-" * 35)
for item in page_info["data"]:
print(f" #{item['id']:3d} {item['name']:<12} ₹{item['price']:,}")
if page_info['page'] >= 3:
print("\n ... (remaining pages on demand)")
break
# OUTPUT:
🛒 Product Catalog (5 per page)
==================================================
📄 Page 1/5 (showing 5 of 21 items)
-----------------------------------
# 1 Product 1 ₹100
# 2 Product 2 ₹200
# 3 Product 3 ₹300
# 4 Product 4 ₹400
# 5 Product 5 ₹500
📄 Page 2/5 (showing 5 of 21 items)
-----------------------------------
# 6 Product 6 ₹600
# 7 Product 7 ₹700
# 8 Product 8 ₹800
# 9 Product 9 ₹900
# 10 Product 10 ₹1,000
📄 Page 3/5 (showing 5 of 21 items)
-----------------------------------
# 11 Product 11 ₹1,100
# 12 Product 12 ₹1,200
# 13 Product 13 ₹1,300
# 14 Product 14 ₹1,400
# 15 Product 15 ₹1,500
... (remaining pages on demand)
🔟 Generator .throw() aur .close()
# ─────────────────────────────────────
# Generator lifecycle control
# ─────────────────────────────────────
def managed_gen():
"""Generator with lifecycle management"""
print(" 🟢 Generator started")
try:
i = 0
while True:
try:
i += 1
yield i
except ValueError as e:
print(f" ⚠️ Exception received: {e}")
yield -1 # Error value yield karo
except GeneratorExit:
print(" 🔴 Generator closing gracefully...")
finally:
print(" 🏁 Cleanup done!")
gen = managed_gen()
print(f"Value 1: {next(gen)}")
print(f"Value 2: {next(gen)}")
# Exception throw karo
print(f"After throw: {gen.throw(ValueError, 'Custom error!')}")
print(f"Value after error: {next(gen)}")
# Generator band karo
gen.close()
print("Generator closed!")
| Value 1 | 1 |
| Value 2 | 2 |
| ⚠️ Exception received | Custom error! |
| After throw | -1 |
| Value after error | 4 |
📊 Generator vs List vs Iterator Comparison
| Feature | List | Generator |
|---|
| Syntax | [x for x in ..] | (x for x in ..) |
| Memory | All at once | One at a time |
| Access | Random index | Sequential only |
| Reusable | Yes (iterate | No (once exhausted) |
| multiple times) | |
| len() | Yes | No |
| Suitable for | < 10k items, | Large/infinite data, |
| need indexing | one-pass processing |
💡 Built-in Generator Functions
# ─────────────────────────────────────
# Python ke built-in generator-like functions
# ─────────────────────────────────────
# range() — numbers generate karta hai lazily
r = range(1, 6)
print(f"range type: {type(r)}")
print(f"range values: {list(r)}")
# map() — transformation
mapped = map(lambda x: x*2, [1, 2, 3, 4, 5])
print(f"\nmap type: {type(mapped)}")
print(f"mapped values: {list(mapped)}")
# filter() — filtering
filtered = filter(lambda x: x > 3, [1, 2, 3, 4, 5])
print(f"\nfilter type: {type(filtered)}")
print(f"filtered values: {list(filtered)}")
# zip() — multiple iterables pair karna
names = ["Rahul", "Priya", "Amit"]
scores = [85, 92, 78]
zipped = zip(names, scores)
print(f"\nzip type: {type(zipped)}")
print(f"zipped values: {list(zip(names, scores))}")
# enumerate() — index + value
fruits = ["Apple", "Banana", "Cherry"]
enumerated = enumerate(fruits, start=1)
print(f"\nenumerate:")
for idx, fruit in enumerate(fruits, start=1):
print(f" {idx}. {fruit}")
| range type | <class 'range'> |
| range values | [1, 2, 3, 4, 5] |
| map type | <class 'map'> |
| mapped values | [2, 4, 6, 8, 10] |
| filter type | <class 'filter'> |
| filtered values | [4, 5] |
| zip type | <class 'zip'> |
| zipped values | [('Rahul', 85), ('Priya', 92), ('Amit', 78)] |
🏗️ Complete Real-World Example: Stock Price Monitor
# ─────────────────────────────────────
# 📈 Stock Price Generator — real-time simulation
# ─────────────────────────────────────
import random
def stock_price_feed(symbol, initial_price, volatility=0.02):
"""
Stock prices simulate karta hai infinitely.
Generator hai — ek ek price yield karta hai.
"""
price = initial_price
while True:
# Random change: -volatility to +volatility
change_pct = random.uniform(-volatility, volatility)
price = round(price * (1 + change_pct), 2)
yield price
def moving_average(prices, window=5):
"""
Moving average calculate karta hai.
Generator pipeline mein kaam karta hai.
"""
window_data = []
for price in prices:
window_data.append(price)
if len(window_data) > window:
window_data.pop(0)
avg = sum(window_data) / len(window_data)
yield round(avg, 2)
def alert_on_change(prices, initial, threshold_pct=3.0):
"""
Significant price change pe alert yield karta hai.
"""
prev_price = initial
for price in prices:
change_pct = abs((price - prev_price) / prev_price) * 100
status = "🔴 DOWN" if price < prev_price else "🟢 UP"
alert = f"⚠️ ALERT!" if change_pct >= threshold_pct else ""
yield {
"price": price,
"change_pct": round(change_pct, 2),
"direction": status,
"alert": alert
}
prev_price = price
# Simulate 15 price ticks
random.seed(2026)
INITIAL = 1500.00 # ₹1500 initial price
prices = stock_price_feed("TCS", INITIAL, volatility=0.025)
avg_prices = moving_average(prices, window=3)
# Note: Can't pipe generators directly when one transforms the other
# in real pipeline — use separate generators per stream
random.seed(2026)
raw_prices = stock_price_feed("TCS", INITIAL, volatility=0.025)
print("📈 TCS Stock Monitor (Live Simulation)")
print("=" * 55)
print(f" {'Tick':<6} {'Price':>10} {'Change':>8} {'Status':<12} {'Alert'}")
print("-" * 55)
prev = INITIAL
for tick in range(1, 16):
price = next(raw_prices)
change = price - prev
change_pct = (change / prev) * 100
direction = "🟢 UP " if change >= 0 else "🔴 DOWN"
alert = "⚠️ ALERT!" if abs(change_pct) >= 2.0 else ""
print(f" {tick:<6} ₹{price:>9,.2f} {change_pct:>+7.2f}% {direction} {alert}")
prev = price
print("=" * 55)
# OUTPUT:
📈 TCS Stock Monitor (Live Simulation)
=======================================================
Tick Price Change Status Alert
-------------------------------------------------------
1 ₹1,505.25 +0.35% 🟢 UP
2 ₹1,484.13 -1.40% 🔴 DOWN
3 ₹1,515.82 +2.13% 🟢 UP ⚠️ ALERT!
4 ₹1,498.64 -1.13% 🔴 DOWN
5 ₹1,523.77 +1.68% 🟢 UP
6 ₹1,489.20 -2.27% 🔴 DOWN ⚠️ ALERT!
7 ₹1,502.89 +0.92% 🟢 UP
8 ₹1,478.45 -1.63% 🔴 DOWN
9 ₹1,512.34 +2.29% 🟢 UP ⚠️ ALERT!
10 ₹1,495.67 -1.10% 🔴 DOWN
11 ₹1,508.92 +0.88% 🟢 UP
12 ₹1,482.13 -1.78% 🔴 DOWN
13 ₹1,518.76 +2.47% 🟢 UP ⚠️ ALERT!
14 ₹1,501.45 -1.14% 🔴 DOWN
15 ₹1,496.23 -0.35% 🔴 DOWN
=======================================================
❓ Interview Questions
Q1: Generator aur normal function mein kya fark hai?
- Normal function: return se ek value deta hai aur khatam ho jaata hai - Generator function: yield se value deta hai aur pause hota hai — next call pe resume
Q2: Generator exhausted hone ke baad kya hota hai?
gen = (x for x in range(3))
list(gen) # [0, 1, 2]
list(gen) # [] ← empty! Already exhausted
Q3: Generator ko reset kaise karein?
Generator reset nahi ho sakta! Dobara generator function call karo:
def my_gen(): yield from range(5)
g = my_gen() # Fresh generator
Q4: yield aur yield from mein kya fark hai?
def gen1():
yield [1, 2, 3] # List as single item yield karta hai
def gen2():
yield from [1, 2, 3] # Each item individually yield karta hai
print(list(gen1())) # [[1, 2, 3]]
print(list(gen2())) # [1, 2, 3]
Q5: Generator expressions kab list comprehension se better hain?
- Large data: 10M+ items process karna ho - One-pass: Data sirf ek baar process karna ho - Infinite: Kabhi khatam na hone wala sequence - Chaining: Multiple transformations pipe karna ho
📋 Summary
┌─────────────────────────────────────────────────────┐
│ GENERATORS — KEY CONCEPTS │
├─────────────────────────────────────────────────────┤
│ ✅ yield se value dena + pause karna │
│ ✅ next() se agle value lo │
│ ✅ for loop automatically next() call karta hai │
│ ✅ Generator expressions: (x for x in ...) │
│ ✅ yield from se delegation/chaining karo │
│ ✅ Infinite sequences easily banao │
│ ✅ Data pipelines banao memory-efficiently │
│ ✅ Large file processing ke liye perfect │
│ ✅ .send() se coroutine-style communication │
│ ✅ Once exhausted — recreate karo (reset nahi) │
└─────────────────────────────────────────────────────┘
⚠️ Common Mistakes
❌ Mistake 1: Generator ko dobara use karna (Reuse after exhaustion)
# GALAT ❌ — Generator ek baar exhaust ho gaya toh khatam!
gen = (x**2 for x in range(5))
print(list(gen)) # [0, 1, 4, 9, 16]
print(list(gen)) # [] ← Empty! Already exhausted
# OUTPUT:
[0, 1, 4, 9, 16]
[]
✅ Fix: Har baar fresh generator banao ya function se call karo:
def squares_gen():
return (x**2 for x in range(5))
print(list(squares_gen())) # [0, 1, 4, 9, 16]
print(list(squares_gen())) # [0, 1, 4, 9, 16] ← Works!
❌ Mistake 2: Generator pe len() call karna
# GALAT ❌ — Generator ki length nahi hoti
gen = (x for x in range(100))
# len(gen) ← TypeError: object of type 'generator' has no len()
✅ Fix: Pehle list mein convert karo ya counter use karo:
count = sum(1 for _ in gen) # Length nikalne ka lazy way
❌ Mistake 3: Generator mein indexing try karna
# GALAT ❌ — Generator sequential hai, random access nahi!
gen = (x for x in range(10))
# gen[5] ← TypeError: 'generator' object is not subscriptable
✅ Fix: itertools.islice() use karo ya list mein convert karo:
from itertools import islice
gen = (x for x in range(10))
fifth = next(islice(gen, 5, 6)) # 5th element (0-indexed)
❌ Mistake 4: yield aur return confuse karna
# GALAT ❌ — return ke baad function khatam ho jaata hai
def not_a_generator():
return [1, 2, 3] # Yeh list return karta hai, generator nahi!
# SAHI ✅ — yield use karo
def is_a_generator():
yield 1
yield 2
yield 3
❌ Mistake 5: Infinite generator ko list() mein convert karna
# GALAT ❌ — Yeh infinite loop ban jaayega, RAM full!
def infinite():
n = 0
while True:
yield n
n += 1
# list(infinite()) ← NEVER DO THIS! Memory crash ho jaayega! 💥
✅ Fix: Hamesha take() ya islice() se limit lagao:
from itertools import islice
first_10 = list(islice(infinite(), 10)) # Safe!
❌ Mistake 6: Generator ko .send() karne se pehle prime na karna
# GALAT ❌ — Pehle next() call karna zaroori hai!
def echo():
while True:
val = yield
print(f"Got: {val}")
gen = echo()
# gen.send("hello") ← TypeError: can't send non-None value to a just-started generator
✅ Fix: Pehle next(gen) ya gen.send(None) se prime karo:
gen = echo()
next(gen) # Prime the generator
gen.send("hello") # Now works! Got: hello
❌ Mistake 7: Generator function call karna par iterate na karna
# GALAT ❌ — Sirf object banta hai, execute nahi hota!
def greet():
print("Hello!")
yield "World"
greet() # Kuch print nahi hoga! Sirf generator object bana
# OUTPUT:
# (nothing printed!)
✅ Fix: next() ya for loop se iterate karo:
gen = greet()
value = next(gen) # Ab "Hello!" print hoga, value = "World"
✅ Key Takeaways
- 🔹 Generator = Lazy Iterator — Values ek ek karke produce hoti hain, sab ek saath memory mein nahi rehti. ATM ki tarah — jitna chahiye utna lo!
- 🔹
yield keyword function ko generator bana deta hai. Har yield pe function pause hota hai aur next call pe resume.
- 🔹 Memory Efficiency — 1 million items ke liye list ~8MB leti hai, generator sirf ~128 bytes! Large data ke liye generators zaroori hain.
- 🔹 Generator Expression —
(x for x in iterable) list comprehension ka lazy version hai. Simple transformations ke liye perfect.
- 🔹
yield from se aap doosre generator ya iterable ko delegate kar sakte ho — nested loops likhne ki zaroorat nahi!
- 🔹 Infinite Sequences generators ke bina possible nahi —
while True: yield pattern se endless data streams banao.
- 🔹 Data Pipelines — Multiple generators ko chain karke complex data transformations memory-efficiently karo. Unix pipes jaisa concept!
- 🔹 One-time use — Generator exhaust hone ke baad reset nahi hota. Fresh generator banao agar dobara chahiye.
- 🔹
.send() method se generator ko values bhej sakte ho — two-way communication! Coroutine pattern ka foundation.
- 🔹 Built-in functions jaise
map(), filter(), zip(), enumerate() sab generator-like objects return karte hain — Python mein lazy evaluation har jagah hai!
❓ FAQ
Q1: Generator aur Iterator mein kya fark hai?
Iterator ek broader concept hai — koi bhi object jo __iter__() aur __next__() implement kare. Generator ek special type ka iterator hai jo yield keyword se automatically banta hai. Matlab: Har generator ek iterator hai, par har iterator generator nahi!
Q2: Kya generator mein return use kar sakte hain?
Haan! Generator mein return use kar sakte ho but woh value directly accessible nahi hoti. return value se StopIteration exception raise hota hai jismein value stored rehti hai:
def gen():
yield 1
yield 2
return "Done!" # StopIteration.value mein jaayega
g = gen()
next(g) # 1
next(g) # 2
try:
next(g)
except StopIteration as e:
print(e.value) # "Done!"
Q3: Generator kab use karna chahiye aur kab list?
Generator use karo jab: Data bohot bada ho (lakhs+ items), sirf ek baar iterate karna ho, ya infinite sequence chahiye. List use karo jab: Random access chahiye (indexing), multiple baar iterate karna ho, ya data chota ho (< 10K items).
Q4: yield from aur simple for loop mein yield karne mein kya fark hai?
Functionally same hain basic cases mein, but yield from exception propagation aur .send() values ko properly delegate karta hai. Complex generator delegation mein yield from zaroori hai:
# Yeh same hain basic case mein:
def gen_a():
for x in sub_gen():
yield x
def gen_b():
yield from sub_gen() # Cleaner + handles .send()/.throw() too!
Q5: Kya ek generator function mein multiple yield ho sakte hain?
Bilkul! Jitne chahein utne yield statements rakh sakte ho. Har yield pe function pause hota hai:
def multi():
yield "First"
yield "Second"
yield "Third"
# list(multi()) → ['First', 'Second', 'Third']
Q6: Generator expressions vs generator functions — kab kya use karein?
Generator expression — simple one-liner transformations ke liye jaise (x*2 for x in data). Generator function — complex logic, multiple yields, conditionals, try/except, state management ke liye. Rule of thumb: Agar ek line mein nahi likh sakte toh function banao!
Q7: Kya generators thread-safe hain?
Nahi! Python generators thread-safe nahi hain by default. Agar multiple threads se ek hi generator use karna hai toh threading.Lock lagao ya har thread ko apna separate generator do.
Q8: Generator se memory kitni bacht ti hai practically?
Bahut zyada! Example: 10 million integers ki list ~400MB RAM leti hai. Same data ka generator sirf ~128 bytes. Yeh 3 million times kam memory hai! Large file processing aur streaming data mein yeh game-changer hai.
🎯 Interview Questions
Q1: Generator function aur normal function mein kya differences hain? Explain with internal working.
Normal function: return se value deta hai, stack frame destroy hota hai, state lost. Generator function: yield se value deta hai, stack frame suspended rehta hai (local variables, instruction pointer sab save). next() call pe exactly wahi se resume hota hai jahan pause hua tha. Internally generator ek state machine hai.
Q2: Generator protocol kya hai? __iter__() aur __next__() kaise kaam karte hain?
Generator object automatically iterator protocol implement karta hai: - __iter__() → Returns self (isliye for loop mein directly use ho sakta hai) - __next__() → Next yield tak execute karke value return karta hai. Jab function end ho jaaye toh StopIteration raise karta hai.
Q3: yield from ka internal mechanism explain karo. Simple loop se kaise different hai?
yield from sub_gen ek transparent bidirectional channel establish karta hai: - Values sub-generator se caller tak directly jaati hain - .send() values caller se sub-generator tak directly jaati hain - .throw() exceptions bhi propagate hote hain - Sub-generator ka return value yield from expression ki value ban jaati hai Simple for loop mein .send() aur .throw() propagation manually handle karna padta.
Q4: Generator-based coroutines aur async/await mein kya relation hai?
Python 3.5 se pehle generators hi coroutines the (@asyncio.coroutine + yield from). async/await syntax usi concept ka cleaner implementation hai. Internally dono similar state machine pattern use karte hain, but async functions dedicated __await__ protocol use karte hain jo generators se separate hai.
Q5: Memory-efficient infinite sequence banao jo prime numbers generate kare.
def primes():
"""Infinite prime number generator using Sieve approach"""
yield 2
composites = {}
candidate = 3
while True:
if candidate not in composites:
yield candidate
composites[candidate * candidate] = [candidate]
else:
for prime in composites[candidate]:
composites.setdefault(prime + candidate, []).append(prime)
del composites[candidate]
candidate += 2
# Usage: list(islice(primes(), 20)) → first 20 primes
Q6: Generator pipeline design pattern explain karo with real-world example.
Generator pipeline mein multiple generators chain hote hain — har stage data transform/filter karta hai: source → filter → transform → aggregate → output Real-world: Log file processing — read lines → parse JSON → filter errors → extract metrics → write report. Har stage ek generator hai, poora pipeline O(1) memory mein run hota hai chahe file GB mein ho!
Q7: .send() method kab use hota hai? Practical use case batao.
.send() se generator ko runtime pe values bhej sakte ho — two-way communication! Use cases: - Running averages: Naye data points bhejo, updated average lo - Coroutine patterns: Event-driven programming - Stateful processing: Generator ka internal state dynamically change karo Important: Pehla call hamesha next(gen) ya .send(None) hona chahiye (priming).
| Aspect | List Comprehension | Generator Expression | |--------|-------------------|---------------------| | Memory | O(n) — sab store | O(1) — ek item at a time | | Speed (small data) | Faster (C-optimized) | Slightly slower (overhead) | | Speed (large data) | Slower (allocation) | Faster (no allocation) | | Reusability | Multiple iterations | Single use only | | Indexing | Yes lst[5] | No — sequential only |
Q9: StopIteration exception kaise handle hoti hai? Kya hota hai agar generator mein unhandled StopIteration ho?
- for loop automatically StopIteration catch karta hai aur loop end karta hai - next() se manually handle karna padta hai via try/except - Python 3.7+: Generator ke andar se StopIteration raise karna RuntimeError deta hai (PEP 479). Yeh common bugs prevent karta hai.
Q10: Real interview scenario — Ek function likho jo multiple sorted generators ko merge karke ek sorted stream produce kare.
import heapq
def merge_sorted(*generators):
"""Multiple sorted generators ko efficiently merge karta hai"""
heap = []
for i, gen in enumerate(generators):
try:
val = next(gen)
heapq.heappush(heap, (val, i, gen))
except StopIteration:
pass
while heap:
val, i, gen = heapq.heappop(heap)
yield val
try:
next_val = next(gen)
heapq.heappush(heap, (next_val, i, gen))
except StopIteration:
pass
# Usage:
# g1 = iter([1, 4, 7])
# g2 = iter([2, 5, 8])
# g3 = iter([3, 6, 9])
# list(merge_sorted(g1, g2, g3)) → [1, 2, 3, 4, 5, 6, 7, 8, 9]
🎉 Python Functions Section Complete! Agle topic ke liye OOP (Classes & Objects) section dekhein!