← Back

Home › Hindi › Generator Hindi में

Python Generator — एक-एक item देने वाले functions

🌐 यह page Hindi में है · Read the English guide →

yieldLazy evaluationMemoryInfinite sequences

yield function को generator बना देता है

Function के अंदर return की जगह yield लिख दो और वो सादा function नहीं रहता — वो generator बन जाता है। फर्क सिर्फ शब्द का नहीं, चलने के तरीके का है।

def countdown(n):
    print("शुरू")
    while n > 0:
        yield n          # यहाँ रुकता है, value देता है
        n -= 1
    print("खत्म")

g = countdown(3)
print(type(g))           # 
print(g)                 # 

# अब तक एक भी line चली नहीं! 'शुरू' तक print नहीं हुआ।

print(next(g))           # शुरू \n 3
print(next(g))           # 2
print(next(g))           # 1
print(next(g))           # खत्म \n StopIteration

दो बातें यहाँ सबसे ज़रूरी हैं। पहली — countdown(3) call करने पर function चलता ही नहीं; सिर्फ एक generator object बनता है। दूसरी — yield पर पहुँचकर function वहीं रुक जाता है, सारी local state के साथ, और अगले next() पर वहीं से आगे बढ़ता है।

Lazy evaluation — memory क्यों बचती है

यही generator का असली फायदा है। List सारी values एक साथ memory में बनाती है; generator एक समय में एक value रखता है।

import sys

# एक करोड़ squares
big_list = [x * x for x in range(10**7)]
print(sys.getsizeof(big_list))      # लगभग 84 MB

big_gen = (x * x for x in range(10**7))
print(sys.getsizeof(big_gen))       # लगभग 200 bytes

# दोनों का total एक ही आएगा
print(sum(big_gen))                 # और list कभी बनी ही नहीं

200 bytes बनाम 84 MB — यही फर्क तय करता है कि आपका script चलेगा या memory खत्म हो जाएगी। ध्यान रहे कि getsizeof सिर्फ generator object का आकार बताता है, क्योंकि उसमें सचमुच लगभग कुछ है ही नहीं।

Infinite sequences — list से नामुमकिन

Generator कभी खत्म न होने वाली sequences बना सकता है, क्योंकि वो कभी सब कुछ एक साथ नहीं बनाता:

def naturals():
    n = 1
    while True:            # infinite loop, पर चलेगा
        yield n
        n += 1

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

def evens():
    yield from naturals()          # एक generator से दूसरे में

# itertools के साथ use करो — ये कभी रुकेंगे नहीं, तो slice चाहिए
import itertools

print(list(itertools.islice(fibonacci(), 10)))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

first_big = next(x for x in naturals() if x > 1000)   # 1001, बिना 1000 items बनाए

आखिरी line generator की ताकत दिखाती है: हज़ारवाँ item चाहिए था, तो सिर्फ हज़ार तक गिना गया — list comprehension में पहले सारे items memory में बन जाते।

Generator expression और comprehension

Bracket बदलने से list comprehension generator expression बन जाता है। बस इतना फर्क, पर नतीजा बड़ा है:

[x * x for x in range(10**7)]     # list — तुरंत सब बन जाता है
(x * x for x in range(10**7))     # generator — lazy

# सबसे ज़्यादा काम का use: तुरंत consume हो जाने वाली जगह
sum(x * x for x in rows)          # parentheses की ज़रूरत भी नहीं
max(len(n) for n in names)
any(n < 0 for n in nums)
all(v > 0 for v in values)
"\n".join(str(r) for r in rows)

नियम आसान है: नतीजा एक बार ही पढ़ना है और किसी sum/max/any/join में जाना है, तो generator लिखो। नतीजे को बाद में दोबारा पढ़ना है, index करना है, या len() चाहिए, तो list चाहिए।

Generator एक बार ही चलता है

यह सबसे आम गलती है, और चुपचाप गलत नतीजा देती है — कोई error नहीं:

gen = (x * x for x in range(5))

print(list(gen))     # [0, 1, 4, 9, 16]
print(list(gen))     # []          — खत्म हो चुका है, दोबारा कुछ नहीं
print(sum(gen))      # 0           — कोई error नहीं, बस गलत जवाब

# दोबारा चाहिए तो दोबारा बनाओ
gen = (x * x for x in range(5))
print(list(gen))     # [0, 1, 4, 9, 16]

# या बार-बार use करना है तो list बना लो
vals = [x * x for x in range(5)]
print(list(vals), list(vals))      # दोनों बार भरा हुआ

इसीलिए दो जगह iterate करना हो तो list बना लो। Generator को बार-बार पढ़ने की कोशिश करना बहुत भ्रमित करने वाला bug है, क्योंकि code बिना शिकायत के चुपचाप खाली नतीजा देता रहता है।

कब generator चुनें, कब list

# generator pipeline — हर step lazy
def read_rows(path):
    with open(path) as f:
        for line in f:
            yield line.strip()

def parse(lines):
    for line in lines:
        if line:
            yield line.split(",")

def keep_valid(rows):
    for row in rows:
        if len(row) == 3:
            yield row

rows = keep_valid(parse(read_rows("big.csv")))   # अब तक कुछ पढ़ा ही नहीं
first = next(rows)                               # पहली valid row — एक लाइन पढ़ी

यह pattern असली data processing में बहुत काम आता है। एक करोड़ लाइन की CSV हो तो भी memory में एक समय में एक लाइन रहती है।

आम गलतियाँ

gen = (x for x in range(3))
len(gen)         # TypeError: object of type 'generator' has no len()
gen[0]           # TypeError: 'generator' object is not subscriptable
list(gen)        # ✅ [0, 1, 2] — index चाहिए तो पहले list बनाओ

🛠️ Practice करो (free, browser में)

पढ़ना काफी नहीं — यही bug खुद fix करके देखो। नीचे के problems browser में ही चलते हैं, कोई install नहीं, कोई login नहीं।

सारे free problems देखो →

अक्सर पूछे जाने वाले सवाल

Generator क्या है?

एक ऐसा function जिसमें yield होता है। वो सारी values एक साथ नहीं बनाता — हर next() पर एक value देता है और वहीं रुक जाता है, सारी local state के साथ। इसलिए memory लगभग नहीं लगती।

yield और return में क्या फर्क है?

return function को खत्म कर देता है और एक value देता है। yield function को वहीं रोकता है, value देता है, और अगले next() पर वहीं से आगे बढ़ने देता है। Generator में return StopIteration का संकेत देता है।

Generator से memory कितनी बचती है?

बहुत। एक करोड़ squares की list लगभग 84 MB लेती है; वही generator expression लगभग 200 bytes, क्योंकि उसमें एक समय में एक value रहती है। बड़े data पर यही फर्क तय करता है कि script चलेगी या नहीं।

क्या generator को दोबारा use कर सकते हैं?

नहीं। वो एक बार ही चलता है और खत्म होने पर चुपचाप खाली नतीजा देता है — कोई error नहीं। दोबारा चाहिए तो दोबारा बनाओ, या बार-बार use करना हो तो list बना लो।

Generator expression और list comprehension में क्या फर्क है?

Bracket बनाम parentheses। [x for x in data] तुरंत पूरी list memory में बनाता है; (x for x in data) lazy generator देता है। नतीजा एक बार ही पढ़ना हो तो generator बेहतर है।

आगे पढ़ो