Intermediate20 minPrerequisite: String Formatting

Iterators & Generators

Memory efficient code likho � lazy evaluation se bade datasets ek ek karke process karo.

Iterators kyun important hain?

Jab tumhare paas bahut bada dataset ho � lakhs ya crores records � toh ek saath load karna memory waste hai. Iterators tumhe ek ek item deta hai jab tum use karo. Generators Python ka shortcut hai jo iterator banana aasan banata hai.

Concept grid dekho:

ITERATOR

next() se ek ek item nikalte hain � list ya koi bhi collection se one by one access.

GENERATOR

yield keyword se function khud iterator ban jaata hai � bina class likhe.

LAZY

Sirf zarurat ka data generate hota hai � memory waste nahi hota.

Iterator: iter() aur next()

Python mein koi bhi iterable (list, tuple, string) ko iterator mein convert kar sakte ho iter() se. Phir next() se ek ek item nikalte ho. Jab items khatam ho jaayein, StopIteration error aata hai.

iterator_basics.py
# Iterator from list
nums = [10, 20, 30]
it = iter(nums)

print(next(it)) # 10
print(next(it)) # 20
print(next(it)) # 30
# print(next(it)) # StopIteration error!

# Iterator from string
word = "hi"
it = iter(word)
print(next(it)) # h
print(next(it)) # i

# Loop automatically uses iterator
for num in [1, 2, 3]:
 print(num) # internally iter() aur next() use hota hai

Har for loop internally iterator pattern use karta hai. Tumhe manually iter() aur next() likhne ki zaroorat nahi hoti, lekin samajhna important hai.

Iterator protocol: Koi bhi object iterator hai agar usme __iter__() aur __next__() methods hain. Lists, tuples, strings � sab iterables hain kyunki inke paas __iter__() hai.

Generator function: yield se magic

Generator function ek normal function hai jo return ki jagah yield use karta hai. Jab tum function call karte ho, yeh ek iterator return karta hai. Har yield value ko pause karta hai aur next call pe wapas se chalu hota hai.

generator_countdown.py
# Generator function
def countdown(n):
 while n > 0:
 yield n
 n -= 1

# Use in for loop
for num in countdown(5):
 print(num) # 5, 4, 3, 2, 1

# Manual iteration
gen = countdown(3)
print(next(gen)) # 3
print(next(gen)) # 2
print(next(gen)) # 1

yield n value return karta hai aur function ko freeze karta hai. Jab next call hota hai, function wahan se chalu hota hai jahan yield tha � n -= 1 se.

Generator expression: list comprehension jaisa

Generator expression list comprehension jaisa hi hai, lekin brackets ki jagah parentheses use hota hai. Yeh lazy hai � sirf jab next() call ho tabhi value generate hoti hai.

generator_expression.py
# List comprehension (eager � sab ek saath)
squares_list = [x**2 for x in range(1000000)] # Memory heavy!

# Generator expression (lazy � ek ek karke)
squares_gen = (x**2 for x in range(1000000)) # Memory efficient!

print(next(squares_gen)) # 0
print(next(squares_gen)) # 1
print(next(squares_gen)) # 4

# Use in for loop
for sq in (x**2 for x in range(5)):
 print(sq) # 0, 1, 4, 9, 16

10 lakh items ka list banana memory le sakta hai, lekin generator expression sirf ek time pe ek item hold karta hai. Isliye bade data ke liye generators best hain.

Memory check: Generator ek baar iterate ho jaaye toh dubara nahi chalega. Dobara use karna ho toh function dobara call karo ya list mein convert karo list(generator).

Real-world use cases

Generators data analysis mein bahut useful hain � especially jab CSV files ya databases se data padhte ho:

generator_practical.py
# Read large file line by line
def read_large_file(filepath):
 with open(filepath, 'r') as f:
 for line in f:
 yield line.strip()

# Infinite counter
def infinite_counter(start=0):
 while True:
 yield start
 start += 1

# Filter with generator
def even_numbers(nums):
 for n in nums:
 if n % 2 == 0:
 yield n

# Use
counter = infinite_counter(10)
print(next(counter)) # 10
print(next(counter)) # 11

evens = even_numbers(range(20))
print(list(evens)) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Generator pattern tabs useful hai jab data bahut bada ho ya infinite stream ho � jaise live data feeds, log streaming, ya sensor data processing.

Concept grid: quick recap

ITERATOR

iter() collection se iterator banata hai, next() ek ek item deta hai.

GENERATOR

yield se function iterator ban jaata hai � pause aur resume hota hai.

LAZY EVALUATION

Generator sirf jab zarurat ho tabhi value generate karta hai � memory safe.

Try it: Fibonacci GeneratorCode edit karo aur pehle 10 fibonacci numbers print karo
Run Python dabayein

Quick check

Generator function likho jo 1 se 5 tak numbers yield kare.

range(1, 6) se 1 se 5 tak numbers milenge. Har number ko yield karo loop ke andar.

Common mistakes

Iterators & Generators complete?

Great. Ab OOP (Object-Oriented Programming) seekho � classes, objects, inheritance, aur Pythonic code design.