Question bank — Generator expressions — memory efficiency
1.2.36 · D5· Coding › Introduction to Programming (Python) › Generator expressions — memory efficiency
Yeh bank generator expressions ke conceptual traps drill karta hai — arithmetic nahi. Har item ek misconception ya ek boundary case ko target karta hai. Related ideas: Lazy evaluation, Iterators and the iterator protocol, List comprehensions, Big-O space complexity.
Shuru karne se pehle, ek shared vocabulary reminder taaki koi bhi word use hone se pehle clearly define ho jaye:
True ya false — justify karo
Ek generator expression apna loop body likhte hi run kar leta hai.
(x*x for x in ...) likhne se sirf ek generator object banta hai; loop body baad mein chalta hai, ek step per value pull hone par — yahi Lazy evaluation hai.(x for x in range(3)) aur [x for x in range(3)] same amount of memory use karte hain.
Ek generator expression ko jitni baar chaaho iterate kar sakte ho.
Comprehension ke around parentheses lagane se hamesha generator banta hai.
(expr) sirf grouping hai; tumhe andar for chahiye, e.g. (expr for item in it). (5) number 5 hai, generator nahi.if filter wala generator phir bhi source ke har element ko visit karta hai.
Generator expression par len() call karne se pata chalta hai kitne items produce honge.
len(g) TypeError raise karta hai.Agar tum kabhi generator iterate nahi karte, to uska loop body bilkul execute nahi hota.
list(g) se generator ko list mein convert karna memory savings wapas deta hai.
list(g) sab kuch materialise kar deta hai, isliye tum wapas memory par aa jaate ho — tumne laziness phenk di.Error dhundho
g = (x*x for x in range(5))
print(g[0])::: Error — generators subscriptable nahi hote. g[0] TypeError raise karta hai; tumhe next(g) ya iterate use karna hoga, kyunki koi stored element hai hi nahi jise index kar sako.
g = (x for x in range(3))
a = list(g)
b = list(g) # expecting [0,1,2] again::: b hai [], naa ki [0,1,2]. Pehle list(g) ne generator exhaust kar diya, isliye doosra pass kuch nahi dekhta — yeh ek classic single-pass trap hai.
total = sum(x for x in range(10) if x % 2)
avg = total / len(x for x in range(10) if x % 2)::: Generator par len(...) TypeError raise karta hai, aur conceptually bhi yeh galat hai: generator ki koi length nahi hoti. Agar tumhe sum aur count dono chahiye to pehle values ko list mein store karo.
def make():
return (line for line in open("data.txt"))
gen = make()
# ... baad mein, file kahin aur already close ho gayi ...
first = next(gen)::: Generator lazy hai, isliye file tab read hoti hai jab consume ki jaaye, naa ki jab make() return hua. Agar next call karne se pehle file close ho gayi, toh "read of closed file" error aata hai — laziness kab side-effects hote hain, woh move kar deta hai.
pairs = (a, b for a in range(2) for b in range(2))::: Syntax error — (a, b for ...) ambiguous hai. Tuples yield karne ke liye expression ko parenthesise karna padega: ((a, b) for a in range(2) for b in range(2)).
nums = (x for x in range(3))
if nums:
print("has items")::: if nums hamesha truthy hota hai — ek generator object truthy hota hai chahe empty ho ya exhausted. Truthiness test karna tumhe nahi batata ki uske paas values hain ya nahi; tumhe next() try karna padega.
Why questions
Generator ki memory kyun hoti hai chahe woh kitni bhi values yield kare?
Lazy file filter par next(error_lines) sirf file ka kuch hissa padhke kyun ruk sakta hai?
next return karta hai aur koi aur line nahi padhi jaati.sum(x*x for x in range(10**6)) memory mein list version se better kyun hai lekin speed mein zaroori nahi?
sum(x*x for x in data) mein inner parentheses kyun drop kar sakte ho?
sum((...)) ko sum(...) likha ja sakta hai.if cond se filter karne wala generator kabhi kabhi "slow to start" kyun lagta hai?
next yield karne se pehle bahut saare rejected items walk karta hai — kaam defer hota hai, skip nahi.Generator ko variable mein store karna uski values ko consume hone se kyun protect nahi karta?
for, sum, ya stray list) permanently shared position aage move kar deta hai.Edge cases
list(x for x in range(0)) kya produce karta hai?
[] — empty range kuch yield nahi karta, isliye generator born hote hi exhausted hota hai; yeh zero-length boundary hai, aur yeh ek valid, error-free result hai.g puri tarah exhausted hone ke baad next(g) kya karta hai?
StopIteration raise karta hai — wahi signal jo for loop silently catch karta hai yeh jaanne ke liye ki use ruk jaana chahiye.Kya ek generator expression infinite ho sakta hai, aur kya woh safe hai?
(x for x in itertools.count()); yeh safe hai sirf tab agar tum use kabhi puri tarah materialise nahi karte — list() ya sum() use karna hamesha ke liye loop karega, lekin next() kuch baar karna theek hai.Agar source iterable generator mid-iteration mein change ho jaye to kya hoga?
next calls source ki current state dekhte hain; iteration ke dauran source ko mutate karna items skip ya repeat kar sakta hai aur yeh ek common bug hai.Kya aisa filter jo kuch match nahi karta, jaise (x for x in range(5) if x > 100), error raise karta hai?
list(...) [] deta hai aur next(...) StopIteration raise karta hai.Kya (x for x in []) aur (x for x in range(0)) behaviour mein alag hain?
Recall Ek line ka summary jo saath le jao
Ek generator expression ek lazy, single-pass, -memory recipe hai: yeh demand par compute karta hai, apne source ko ek baar walk karta hai, ek waqt mein ek item hold karta hai, aur ise index nahi kiya ja sakta, len se measure nahi kiya ja sakta, ya replay nahi kiya ja sakta.
Connections
- Lazy evaluation — kyun "compute on demand" yahan har trap ka base hai
- Iterators and the iterator protocol — single-pass /
StopIterationmechanics - List comprehensions — eager
[]cousin jo reuse aur index ho sakta hai - Big-O space complexity — vs distinction
- Generator functions and yield —
def/yieldsibling with the same laziness