Functions Advanced

Generators

yield — Lazy Value Production
💡 Generator ek vending machine jaisa hai jo on-demand cheezein deta hai — normal function ek dabba hai jisme sab kuch pehle se pack hai (poori list). Generator har baar poocho tabhi ek naya item deta hai, kuch bhi advance mein store nahi karta.

Generator function normal function jaisa hi dikhta hai, lekin return ki jagah yield use karta hai — call karne par, ye turant poora execute nahi hota, balki ek "generator object" return karta hai jo values ko LAZILY (on-demand) produce karta hai.

Fayda: memory efficiency — agar tumhe 10 million numbers process karne hain, generator ek time par ek hi number memory mein rakhta hai, poori list ek saath nahi (jo crash bhi kar sakta hai bade datasets ke liye).

def count_up_to(n):
    i = 1
    while i <= n:
        yield i    # value do, lekin function "pause" ho jaata hai yahin
        i += 1

for num in count_up_to(5):
    print(num)   # 1 2 3 4 5 — ek time par ek generate hota hai

# Memory comparison:
def squares_list(n):        # poori list memory mein
    return [x**2 for x in range(n)]

def squares_gen(n):          # ek time par ek value
    for x in range(n):
        yield x ** 2

# squares_list(10_000_000) bahut memory legi
# squares_gen(10_000_000) almost koi memory nahi legi turant
Generator ek vending machine jaisa hai jo on-demand cheezein deta hai — normal function ek dabba hai jisme sab kuch pehle se pack hai (poori list). Generator har baar poocho tabhi ek naya item deta hai, kuch bhi advance mein store nahi karta.
1 / 2
⚡ Quick Recap
  • yield = value do aur pause karo, return jaisa nahi (poora khatam nahi hota)
  • Lazy evaluation — memory-efficient bade/infinite sequences ke liye
  • for loop generator ko automatically consume karta hai
On this page (2 subtopics)

Generator function (yield wala) aur generator expression ((x for x in ...)) dono lazy hain, same underlying concept — bas syntax alag hai. Expression simple cases ke liye, function complex logic ke liye.

# Generator function:
def squares(n):
    for x in range(n):
        yield x ** 2

# Generator expression — same result, simpler cases ke liye:
squares_expr = (x ** 2 for x in range(5))

for val in squares_expr:
    print(val)   # 0 1 4 9 16
💡Tip: Generator expression ek list comprehension jaisa hi dikhta hai, bas [] ki jagah () — lekin behavior completely different hai (lazy vs eager).

Generators sirf values produce nahi karte, values RECEIVE bhi kar sakte hain generator.send(value) se — advanced pattern, coroutines/pipelines banane mein use hota hai.

def echo():
    while True:
        received = yield
        print(f"Received: {received}")

gen = echo()
next(gen)          # generator ko "start" karo (pehle yield tak)
gen.send("Hello")   # "Received: Hello"
gen.send("World")   # "Received: World"
💡Tip: send() ek advanced/rare pattern hai — zyadatar real-world code mein generators sirf yield karte hain (values produce), send() ka use bahut kam hota hai, mostly custom coroutine-jaisi systems mein.