The Story That Explains Iterators & Generators 📺Iterators और Generators समझाने वाली कहानी 📺一个讲清迭代器与生成器的故事 📺
Now compare that with streaming. The streaming service does not send you all 100 episodes. It plays episode 1 when you press play, then episode 2 only when you finish episode 1. You get one episode at a time — on demand. If you stop at episode 3, episodes 4 to 100 were never even loaded.
The DVD boxset is a list (an iterable that holds everything in memory). Streaming is a generator (it makes values one at a time, only when asked). Same 100 episodes — very different memory use.
अब इसकी तुलना streaming से कीजिए। Streaming service आपको सारे 100 episodes नहीं भेजती। Play दबाने पर episode 1 चलता है, फिर episode 1 ख़त्म होने पर ही episode 2। आपको एक बार में एक episode मिलता है — माँगने पर। अगर आप episode 3 पर रुक जाएँ, तो episode 4 से 100 कभी load ही नहीं हुए।
DVD boxset एक list है (iterable जो सब कुछ memory में रखती है)। Streaming एक generator है (माँगने पर एक-एक value बनाता है)। वही 100 episodes — memory का बिल्कुल अलग इस्तेमाल।
再对比一下 在线播放。流媒体服务不会把 100 集一次全发给你。你按下播放时它播第 1 集,看完第 1 集后才播第 2 集。你一次只拿到一集 — 按需获取。如果你看到第 3 集就停了,第 4 到 100 集根本没有加载过。
DVD 套装就是一个 列表(把所有东西都放在内存里的可迭代对象)。在线播放就是一个 生成器(只在被请求时才一次产生一个值)。同样的 100 集 — 内存占用完全不同。
In Python, iterables, iterators and generators are three closely related ideas that beginners often mix up. All three let you loop through data — but the way they store, compute and hand out values is very different. Master them and you can handle huge files as easily as a handful of rows.
Python में iterables, iterators और generators तीन जुड़े हुए विचार हैं जिन्हें beginners अक्सर मिला देते हैं। तीनों से आप data पर loop चला सकते हैं — पर values रखने, निकालने और देने का उनका तरीक़ा बहुत अलग है। इन्हें समझ लीजिए तो आप बहुत बड़ी files भी कुछ rows जितनी आसानी से सँभाल लेंगे।
在 Python 中,可迭代对象、迭代器 和 生成器 是三个密切相关、初学者常常混淆的概念。它们都能让你遍历数据 — 但存储、计算和交出值的方式大不相同。掌握它们,你处理巨大的文件就会像处理几行数据一样轻松。
Every generator is an iterator. Every iterator is an iterable. The reverse is not true — a list is iterable but is not an iterator, and an iterator built with a class is not a generator. Understanding these three nested circles is the whole game.
हर generator एक iterator है। हर iterator एक iterable है। उलटा सच नहीं है — list iterable है पर iterator नहीं, और class से बना iterator generator नहीं है। इन तीन एक-के-अंदर-एक घेरों को समझना ही पूरा खेल है।
每个生成器都是迭代器。每个迭代器都是可迭代对象。反过来不成立 — 列表是可迭代对象但 不是 迭代器,用类写的迭代器也不是生成器。理解这三个嵌套的圆圈就掌握了全部要领。
Look for the interactive boxes: the iter() / next() Lab (Section 03), Generator Step by Step (06), Lazy vs Eager (07), the send() Lab (11), the Bug Doctor (13) and the Pipeline Flow (14). Every result comes from real Python runs.
Interactive डिब्बे खोजें: iter() / next() Lab (Section 03), Generator क़दम-दर-क़दम (06), Lazy बनाम Eager (07), send() Lab (11), Bug Doctor (13) और Pipeline Flow (14)। हर नतीजा असली Python runs से है।
请留意这些互动框:iter() / next() 实验室(第 03 节)、生成器逐步演示(06)、惰性 vs 立即(07)、send() 实验室(11)、Bug 医生(13)和 流水线演示(14)。每个结果都来自真实的 Python 运行。
Containers & Iterables — The Foundation 🧱Containers और Iterables — नींव 🧱容器与可迭代对象 — 基础 🧱
A container is any object that holds a collection of values — list, tuple, dict, set and str are the most common. An iterable is any object that has an __iter__ method, which means Python knows how to get an iterator from it.
Container ऐसा object है जो values का संग्रह रखता है — list, tuple, dict, set और str सबसे आम हैं। Iterable ऐसा object है जिसमें __iter__ method हो, यानी Python उससे iterator निकालना जानता है।
容器 是任何保存一组值的对象 — 最常见的是 list、tuple、dict、set 和 str。可迭代对象 是任何有 __iter__ 方法的对象,也就是说 Python 知道如何从它得到一个迭代器。
__iter__() method that returns an iterator.__iter__() method होना चाहिए जो iterator लौटाए।__iter__() 方法。iter() behind the scenes. The in operator uses __contains__ if the object has one, and falls back to iter() if not.iter() बुलाते हैं। in operator object में __contains__ हो तो उसे लेता है, न हो तो iter() पर लौट आता है।iter()。in 运算符如果对象有 __contains__ 就用它,没有才退回使用 iter()。next() on it directly fails.next() बुलाना fail होता है।next() 会失败。# A list is iterable - but is NOT an iterator
list1 = [10, 20, 30]
print(hasattr(list1, '__iter__')) # True - it IS iterable
print(hasattr(list1, '__next__')) # False - it is NOT an iterator
# This FAILS:
try:
next(list1)
except TypeError as e:
print("TypeError:", e)
# This WORKS - first get an iterator using iter()
it = iter(list1)
print(next(it)) # 10
print(next(it)) # 20
print(next(it)) # 30
try:
next(it)
except StopIteration:
print("StopIteration - nothing left")
In the original, next(list1) crashed the program on line 8, so the lines after it never ran — and the last next(it) would crash again with StopIteration. Both are now inside try/except, so you can see every output. Rule 2 was also corrected: in only falls back to iter() when there is no __contains__.
मूल में next(list1) ने line 8 पर program crash कर दिया था, इसलिए उसके बाद की lines कभी नहीं चलीं — और आख़िरी next(it) फिर StopIteration से crash होता। दोनों अब try/except के अंदर हैं, ताकि आप हर output देख सकें। नियम 2 भी ठीक किया: in सिर्फ़ तब iter() पर लौटता है जब __contains__ न हो।
在原文中,next(list1) 在第 8 行就让程序崩溃了,后面的代码根本没有运行 — 而最后的 next(it) 还会因为 StopIteration 再次崩溃。现在两者都放进了 try/except,你能看到每一个输出。规则 2 也已修正:只有在没有 __contains__ 时,in 才会退回使用 iter()。
Almost every beginner writes next(my_list) at some point and gets a TypeError. The list knows how to give you an iterator, but it is not one itself. You must call iter() first. This is what lets you loop over the same list many times — each for asks for a fresh iterator.
लगभग हर beginner कभी न कभी next(my_list) लिखता है और TypeError पाता है। List को iterator देना आता है, पर वह ख़ुद iterator नहीं है। पहले iter() बुलाना होगा। इसी वजह से आप एक ही list पर कई बार loop चला सकते हैं — हर for नया iterator माँगता है।
几乎每个初学者都会在某个时候写下 next(my_list),然后得到 TypeError。列表 知道如何 给你一个迭代器,但它本身不是迭代器。你必须先调用 iter()。正因如此,你才能多次遍历同一个列表 — 每个 for 都会要一个新的迭代器。
Iterators — The Stateful Cursor 👆Iterators — याद रखने वाला Cursor 👆迭代器 — 有状态的游标 👆
An iterator is an object that remembers where it is in the sequence. It has two methods: __iter__ (which returns itself) and __next__ (which returns the next value, or raises StopIteration when there is nothing left).
Iterator ऐसा object है जो याद रखता है कि sequence में वह कहाँ है। इसमें दो methods होते हैं: __iter__ (जो ख़ुद को लौटाता है) और __next__ (जो अगली value लौटाता है, या कुछ न बचे तो StopIteration raise करता है)।
迭代器 是一个记得自己在序列中 处于何处 的对象。它有两个方法:__iter__(返回自身)和 __next__(返回下一个值,没有值时抛出 StopIteration)。
__iter__ simply returns the object itself. That is what makes it work in a for loop — the loop calls iter(), gets back the same iterator, and starts using it.__iter__ बस object ख़ुद लौटाता है। इसी से वह for loop में चलता है — loop iter() बुलाता है, वही iterator वापस पाता है, और इस्तेमाल शुरू करता है।__iter__ 只是返回对象本身。正因如此它才能用在 for 循环中 — 循环调用 iter(),拿回同一个迭代器,然后开始使用。next() function. Returns the next item and moves the internal position forward. When there are no more values, it must raise StopIteration.next() function इसे बुलाता है। अगला item लौटाता है और अंदर की position आगे बढ़ाता है। जब values ख़त्म हों, तो इसे StopIteration raise करना ही होगा।next() 调用。返回下一个元素并把内部位置向前移动。没有更多值时,必须抛出 StopIteration。StopIteration, it is exhausted. To go again, build a new one from the original iterable.StopIteration के बाद वह ख़त्म हो जाता है। फिर से चलाने के लिए मूल iterable से नया बनाइए।StopIteration 之后它就耗尽了。要再来一遍,就从原来的可迭代对象新建一个。If you call iter() on something that is already an iterator, you get the same object back — not a fresh one. That is why you cannot "reset" an iterator by calling iter() on it again:
अगर आप iter() किसी ऐसी चीज़ पर बुलाएँ जो पहले से iterator है, तो वही object वापस मिलता है — नया नहीं। इसीलिए आप iterator पर फिर iter() बुलाकर उसे "reset" नहीं कर सकते:
如果你对一个 已经 是迭代器的对象调用 iter(),得到的是同一个对象 — 而不是新的。这就是为什么你不能通过再次调用 iter() 来"重置"迭代器:
nums = [1, 2, 3]
it = iter(nums)
print(iter(it) is it) # True - an iterator's __iter__ returns itself
print(iter(nums) is nums) # False - a list gives a NEW iterator each time
print(list(it)) # [1, 2, 3] - uses up the iterator
print(list(it)) # [] - already exhausted
print(list(nums)) # [1, 2, 3] - the list itself is untouched
🧪 Simulator: iter() / next() Lab🧪 Simulator: iter() / next() Lab🧪 模拟器:iter() / next() 实验室
Pick an object, then run lines in any order. Watch the cursor move. With a list or a string, iter() makes a new cursor every time; with an iterator, obj and it share one cursor. Checked against Python on 400 random sequences.
एक object चुनिए, फिर lines किसी भी क्रम में चलाइए। Cursor को चलते देखिए। List या string के साथ iter() हर बार नया cursor बनाता है; iterator के साथ obj और it एक ही cursor share करते हैं। 400 random sequences पर Python से जाँचा गया।
选一个对象,然后按任意顺序运行代码。观察游标的移动。对列表或字符串,iter() 每次都创建新游标;对迭代器,obj 和 it 共用同一个游标。已在 400 个随机序列上与 Python 对照。
Building a Custom Iterator — MyRange 🛠️Custom Iterator बनाना — MyRange 🛠️自己写一个迭代器 — MyRange 🛠️
Let's build our own iterator that works like Python 2's xrange() — producing numbers one at a time, without ever holding the full list in memory.
चलिए अपना iterator बनाते हैं जो Python 2 के xrange() जैसा काम करे — एक-एक करके नंबर दे, पूरी list कभी memory में रखे बिना।
我们来写一个自己的迭代器,让它像 Python 2 的 xrange() 一样工作 — 一次产生一个数字,从不把整个列表放进内存。
class MyRange:
"""A minimal version of Python 2's xrange as a class-based iterator."""
def __init__(self, start, end):
self.value = start
self.end = end
def __iter__(self):
return self # iterators return themselves
def __next__(self):
if self.value >= self.end:
raise StopIteration
current = self.value
self.value += 1
return current
# Use it exactly like the built-in range
r = MyRange(1, 5)
for num in r:
print(num, end=' ')
That is about 15 lines of code for a lazy sequence. Powerful — but long. Python has a much shorter route to the same behaviour: the yield keyword.
Lazy sequence के लिए लगभग 15 lines का code। ताक़तवर — पर लंबा। Python में यही व्यवहार पाने का बहुत छोटा रास्ता है: yield keyword।
一个惰性序列大约要 15 行代码。很强大 — 但太长了。Python 有一条短得多的路能得到同样的效果:yield 关键字。
Enter Generators — Iterators With Superpowers ⚡Generators — ख़ास ताक़त वाले Iterators ⚡生成器登场 — 拥有超能力的迭代器 ⚡
yield statement, it freezes mid-way, hands the value back to the caller and keeps its local variables. The next call to next() wakes it up, and it continues exactly where it stopped.yield is a pause button that also delivers a package.yield statement पर पहुँचकर वह बीच में ही रुक जाता है, value caller को देता है और अपने local variables सँभाल कर रखता है। अगली next() call उसे जगाती है, और वह ठीक वहीं से आगे बढ़ता है जहाँ रुका था।yield एक pause button है जो साथ में package भी देता है।yield 语句时会 中途冻结,把值交给调用者,并保留自己的局部变量。下一次 next() 调用会唤醒它,让它从停下的地方继续。yield 是一个还会顺便送出包裹的暂停键。A generator is a special kind of iterator built from a function that contains at least one yield. As soon as Python sees yield anywhere in a function body, that function becomes a generator function — calling it does not run the code; it just returns a generator object.
Generator एक ख़ास तरह का iterator है जो ऐसे function से बनता है जिसमें कम से कम एक yield हो। Python जैसे ही function body में कहीं yield देखता है, वह function generator function बन जाता है — उसे बुलाने पर code नहीं चलता; बस एक generator object मिलता है।
生成器 是一种特殊的迭代器,由至少包含一个 yield 的函数构建。只要 Python 在函数体中任何地方看到 yield,这个函数就成为 生成器函数 — 调用它不会执行代码,只会返回一个生成器对象。
def my_range(start, end):
"""A generator version of MyRange - 4 lines instead of 15."""
while start < end:
yield start
start += 1
# Calling the function does NOT run the body
gen = my_range(1, 5)
print(gen)
# Values are produced only on demand
for num in gen:
print(num, end=' ')
No class. No __iter__. No __next__. No manual StopIteration. Just yield, and Python handles the rest. When a generator function reaches the end of its body (or a bare return), Python raises StopIteration for you.
न class। न __iter__। न __next__। न हाथ से StopIteration। बस yield, और बाक़ी Python सँभालता है। जब generator function अपनी body के अंत (या ख़ाली return) तक पहुँचता है, Python आपके लिए StopIteration raise करता है।
不用写类,不用 __iter__,不用 __next__,也不用手动 StopIteration。只要 yield,其余的都交给 Python。当生成器函数执行到函数体末尾(或一个空的 return)时,Python 会替你抛出 StopIteration。
How yield Actually Works — Step by Step ⏯️yield असल में कैसे काम करता है — क़दम-दर-क़दम ⏯️yield 究竟如何工作 — 逐步拆解 ⏯️
Each next() wakes the frozen function, runs it to the next yield, then freezes it again. Local variables survive between calls.
हर next() जमे हुए function को जगाता है, अगले yield तक चलाता है, फिर जमा देता है। Calls के बीच local variables बचे रहते हैं।
每次 next() 都会唤醒冻结的函数,运行到下一个 yield 后再次冻结。局部变量在调用之间保留下来。
gen = my_range(1, 5). Python sees yield in the body and returns a generator object without running a single line.gen = my_range(1, 5) बुलाते हैं। Python body में yield देखकर एक भी line चलाए बिना generator object लौटा देता है।gen = my_range(1, 5)。Python 看到函数体里有 yield,于是 一行代码都不运行,直接返回一个生成器对象。for loop calls next(gen). Now the body starts — it checks the while, reaches yield start and freezes. The value 1 is handed back.for loop next(gen) बुलाता है। अब body शुरू होती है — while जाँचती है, yield start तक पहुँचकर रुक जाती है। Value 1 वापस दी जाती है।for 循环调用 next(gen)。函数体这才开始执行 — 检查 while,到达 yield start 后冻结,交回值 1。yield. start becomes 2. The loop goes round, hits yield again, freezes and hands back 2.yield के बाद वाली line से आगे बढ़ता है। start 2 बनता है। Loop घूमकर फिर yield पर पहुँचता है, रुकता है और 2 देता है।yield 之后 的那一行继续。start 变成 2。循环再转一圈,又遇到 yield,冻结并交回 2。start < end is False. The function ends, and Python raises StopIteration.start < end False है। Function ख़त्म होता है, और Python StopIteration raise करता है।start < end 为 False,函数结束,Python 抛出 StopIteration。for loop catches StopIteration and exits cleanly. Any later next(gen) keeps raising StopIteration — the generator is used up for good.for loop StopIteration पकड़कर साफ़ तरह बाहर निकलता है। बाद की हर next(gen) फिर StopIteration देती है — generator हमेशा के लिए ख़त्म।for 循环捕获 StopIteration 并干净地退出。之后再调用 next(gen) 都会继续抛出 StopIteration — 生成器已经永远耗尽了。👣 Simulator: Generator Step by Step👣 Simulator: Generator क़दम-दर-क़दम👣 模拟器:生成器逐步演示
Recorded with sys.settrace. Watch the my_range frame appear on the call stack at each next(), then disappear at yield — while its locals stay safe in the frozen box.
sys.settrace से रिकॉर्ड किया हुआ। हर next() पर my_range का frame call stack पर आता है, फिर yield पर हट जाता है — जबकि उसके locals frozen डिब्बे में सुरक्षित रहते हैं।
用 sys.settrace 录制。观察 my_range 的帧在每次 next() 时出现在调用栈上,又在 yield 时消失 — 而它的局部变量安全地保存在 冻结 框里。
Generator Expressions — Comprehensions With Parentheses 🫧Generator Expressions — Parentheses वाली Comprehensions 🫧生成器表达式 — 带圆括号的推导式 🫧
Just as list comprehensions give you short list creation, generator expressions give you short generator creation. The syntax is the same, except you use parentheses () instead of square brackets [].
जैसे list comprehensions से छोटे में list बनती है, वैसे generator expressions से छोटे में generator बनता है। Syntax वही है, बस square brackets [] की जगह parentheses () लगते हैं।
就像列表推导式能简洁地创建列表,生成器表达式 能简洁地创建生成器。语法完全相同,只是把 方括号 [] 换成 圆括号 ()。
| Propertyगुण属性 | Valueमान值 |
|---|---|
| SyntaxSyntax语法 | [x*x for x in range(10)] |
| Return typeReturn type返回类型 | list |
| Evaluatedकब बनता है何时计算 | All at onceएक साथ सब一次全部 |
| MemoryMemory内存 | Holds all itemsसारे items रखता है保存所有元素 |
| Reusableदोबारा इस्तेमाल可重复使用 | Yesहाँ是 |
| Propertyगुण属性 | Valueमान值 |
|---|---|
| SyntaxSyntax语法 | (x*x for x in range(10)) |
| Return typeReturn type返回类型 | generator |
| Evaluatedकब बनता है何时计算 | Lazily, one at a timeLazy, एक-एक करके惰性地,一次一个 |
| MemoryMemory内存 | Tiny, constantबहुत कम, स्थिर很小,恒定 |
| Reusableदोबारा इस्तेमाल可重复使用 | No — single useनहीं — एक बार否 — 只能用一次 |
# List comprehension - builds the whole list in memory
squares_list = [x * x for x in range(10)]
print(squares_list)
# Generator expression - builds a generator (nothing computed yet)
squares_gen = (x * x for x in range(10))
print(squares_gen)
# Use the values on demand
for sq in squares_gen:
print(sq, end=' ')
🐢🐇 Simulator: Lazy vs Eager🐢🐇 Simulator: Lazy बनाम Eager🐢🐇 模拟器:惰性 vs 立即
Both columns square the numbers with a function sq(x) that announces each computation. The list computes everything first; the generator computes only what the consumer asks for. Change n and the consumer, then press Play. The event order comes from real Python runs.
दोनों columns numbers का square sq(x) function से करते हैं जो हर गणना बताता है। List पहले सब कुछ निकालती है; generator सिर्फ़ उतना निकालता है जितना consumer माँगे। n और consumer बदलिए, फिर Play दबाइए। Events का क्रम असली Python runs से है।
两列都用一个会报告每次计算的函数 sq(x) 来求平方。列表先把 所有 值都算出来;生成器只计算消费者要求的那些。修改 n 和消费者,然后按 Play。事件顺序来自真实的 Python 运行。
Memory & Performance — The Real Difference 📊Memory और Performance — असली फ़र्क़ 📊内存与性能 — 真正的差别 📊
This is where generators earn their keep. Let's measure the memory of a list comprehension and a generator expression doing the same work for 10 million items.
यहीं generators की असली क़ीमत दिखती है। चलिए 10 million items पर एक ही काम करने वाली list comprehension और generator expression की memory नापें।
生成器的价值正体现在这里。我们来测量一下:对 1000 万个元素做同样计算时,列表推导式和生成器表达式各占多少内存。
import sys
# List comprehension - stores everything
lst = [x * 2 for x in range(10_000_000)]
print(f"List memory: {sys.getsizeof(lst):,} bytes")
# Generator expression - stores almost nothing
gen = (x * 2 for x in range(10_000_000))
print(f"Generator memory: {sys.getsizeof(gen):,} bytes")
Same computation, same 10 million values — but the generator keeps only the recipe, not the results.
वही गणना, वही 10 million values — पर generator सिर्फ़ तरीक़ा रखता है, नतीजे नहीं।
同样的计算、同样的 1000 万个值 — 但生成器只保存"配方",不保存结果。
The generator keeps only the recipe for making values, so its size stays at a couple of hundred bytes however many values it will yield. Fixed: sys.getsizeof(lst) counts only the list's pointer slots (about 85 MB). The 10 million int objects they point to need memory too — measured on our machine, the whole list used about 409 MB. So the real saving is even larger than the getsizeof numbers suggest.
Generator सिर्फ़ values बनाने का तरीक़ा रखता है, इसलिए उसका size कुछ सौ bytes ही रहता है, चाहे वह कितनी भी values दे। सुधार: sys.getsizeof(lst) सिर्फ़ list के pointer slots गिनता है (लगभग 85 MB)। जिन 10 million int objects की ओर वे इशारा करते हैं, उन्हें भी memory चाहिए — हमारी machine पर नापने पर पूरी list ने लगभग 409 MB लिए। तो असली बचत getsizeof के नंबरों से भी बड़ी है।
生成器只保存产生值的"配方",所以无论最终产生多少个值,它的大小都保持在一两百字节。已修正:sys.getsizeof(lst) 只统计列表的指针槽位(约 85 MB)。这些指针指向的 1000 万个 int 对象同样占用内存 — 在我们的机器上实测,整个列表约占 409 MB。所以真实的节省比 getsizeof 的数字显示的还要大。
Speed Comparison — Summing 10 Million NumbersSpeed की तुलना — 10 Million Numbers का जोड़速度对比 — 对 1000 万个数求和
import time
# List comprehension - build then sum
t = time.perf_counter()
total = sum([x for x in range(10_000_000)])
print(f"List: {time.perf_counter() - t:.2f}s")
# Generator expression - stream directly into sum
t = time.perf_counter()
total = sum(x for x in range(10_000_000))
print(f"Generator: {time.perf_counter() - t:.2f}s")
The original used 100 million numbers and showed fixed times (6.42 s vs 4.87 s). Timings depend on your machine and Python version, so the numbers above are a real run at build time with 10 million numbers. On our machine the generator was faster in every run — but the bigger, more reliable win is memory.
मूल में 100 million numbers और तय समय (6.42 s बनाम 4.87 s) थे। Timings आपकी machine और Python version पर निर्भर करते हैं, इसलिए ऊपर के नंबर build के समय 10 million numbers के साथ असली run हैं। हमारी machine पर हर run में generator तेज़ रहा — पर ज़्यादा बड़ा और भरोसेमंद फ़ायदा memory का है।
原文使用 1 亿个数字,并给出了固定的时间(6.42 秒 vs 4.87 秒)。计时取决于你的机器和 Python 版本,所以上面的数字是构建时用 1000 万个数字真实运行的结果。在我们的机器上,每次运行生成器都更快 — 但更大、更可靠的优势在于内存。
Practical Example 1 — Fibonacci Forever ♾️Practical Example 1 — अंतहीन Fibonacci ♾️实战示例 1 — 无穷的斐波那契 ♾️
A classic use of generators: an infinite sequence. A list of infinite Fibonacci numbers is impossible — a generator is not.
Generators का क्लासिक इस्तेमाल: अंतहीन sequence। अंतहीन Fibonacci numbers की list असंभव है — generator नहीं।
生成器的经典用法:无穷序列。无穷斐波那契数的列表是不可能的 — 生成器却可以。
def fibonacci():
"""An infinite Fibonacci generator."""
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Take the first 10 Fibonacci numbers
fib = fibonacci()
for _ in range(10):
print(next(fib), end=' ')
Combined With itertools.isliceitertools.islice के साथ结合 itertools.islice
from itertools import islice
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Take Fibonacci numbers 20 through 29 without building a big list
first_ten_from_20 = list(islice(fibonacci(), 20, 30))
print(first_ten_from_20)
Practical Example 2 — Reading a Huge File 📜Practical Example 2 — बहुत बड़ी File पढ़ना 📜实战示例 2 — 读取超大文件 📜
Suppose you must scan a 20 GB server log for the word "ERROR". Loading it with file.readlines() would fill your RAM. A generator handles it with only one line in memory at a time. Here it runs on a small sample file, app.log, so you can see real output:
मान लीजिए आपको 20 GB server log में "ERROR" खोजना है। file.readlines() से load करने पर RAM भर जाएगी। Generator एक बार में memory में सिर्फ़ एक line रखकर इसे सँभाल लेता है। असली output दिखाने के लिए यहाँ यह एक छोटी sample file app.log पर चलता है:
假设你要在 20 GB 的服务器日志里查找 "ERROR"。用 file.readlines() 加载会撑爆内存。生成器一次只在内存里保留一行就能搞定。为了展示真实输出,这里在一个小样本文件 app.log 上运行:
def grep_errors(path):
"""Yield every line in a log file that contains 'ERROR'."""
with open(path) as f:
for line in f: # file objects are iterators too!
if 'ERROR' in line:
yield line.rstrip()
# Streams line by line - only one line in memory at a time
errors = grep_errors('app.log')
# Print the first 5 error lines
for line, _ in zip(errors, range(5)):
print(line)
Generators connect. Feed one into another to build a lazy pipeline: lines → filtered → parsed → totals. Each stage pulls a value only when the next stage asks. It is the same idea as Unix pipes — and it lets you process huge data on a laptop. Section 14 shows it step by step.
Generators जुड़ते हैं। एक को दूसरे में डालकर lazy pipeline बनाइए: lines → filtered → parsed → totals। हर stage value तभी खींचता है जब अगला stage माँगे। यह Unix pipes जैसा ही विचार है — और इससे laptop पर भी बहुत बड़ा data process हो जाता है। Section 14 इसे क़दम-दर-क़दम दिखाता है।
生成器可以 串联。把一个接到另一个上,就构成惰性流水线:lines → filtered → parsed → totals。每一级只在下一级请求时才拉取一个值。这和 Unix 管道是同一个思路 — 让你在笔记本上也能处理海量数据。第 14 节会逐步演示。
Advanced — send(), close() and yield from 🎛️Advanced — send(), close() और yield from 🎛️进阶 — send()、close() 与 yield from 🎛️
Generators can do more than produce values. They can also receive them.
Generators सिर्फ़ values बनाते ही नहीं। वे values ले भी सकते हैं।
生成器不仅能产生值,还能 接收 值。
x becomes the value of the paused yield expression. It lets you feed data into a generator.x रुके हुए yield expression की value बन जाता है। इससे आप generator में data डाल सकते हैं।x 成为暂停处 yield 表达式的值。它让你能把数据 送进 生成器。GeneratorExit inside it. Any finally block gets a chance to clean up.GeneratorExit raise करके उसे रोकता है। कोई finally block हो तो उसे साफ़-सफ़ाई का मौक़ा मिलता है।GeneratorExit 来停止它。任何 finally 块都有机会做清理。E inside the generator, at the paused yield. Useful for signalling errors from outside.yield पर exception E raise करता है। बाहर से errors बताने में काम आता है।yield 处抛出异常 E。适合从外部发出错误信号。for … yield loop, and it passes send/throw/return through.for … yield loop से छोटा, और send/throw/return आगे पहुँचाता है।for … yield 循环更简洁,并且会转发 send/throw/return。def echo():
"""A generator that echoes whatever you send into it."""
while True:
received = yield
print(f"Got: {received}")
e = echo()
next(e) # prime the generator (run to the first yield)
e.send('hello') # Got: hello
e.send(42) # Got: 42
e.close() # stops the generator cleanly
# yield from - hand over to a sub-generator
def countdown(n):
while n > 0:
yield n
n -= 1
def combined():
yield from countdown(3)
yield from ['go!', 'done']
print(list(combined()))
🎛️ Simulator: send() Lab🎛️ Simulator: send() Lab🎛️ 模拟器:send() 实验室
Drive the echo() generator yourself. Try send() before priming, prime it with next(), send values, then close it and try again. Checked against Python on 300 random sequences.
echo() generator को ख़ुद चलाइए। Prime करने से पहले send() आज़माइए, next() से prime कीजिए, values भेजिए, फिर close करके दोबारा कोशिश कीजिए। 300 random sequences पर Python से जाँचा गया।
亲手驱动 echo() 生成器。先试试在预激之前 send(),再用 next() 预激,发送一些值,然后关闭它再试一次。已在 300 个随机序列上与 Python 对照。
The Three Ideas Side by Side 🎯तीनों विचार साथ-साथ 🎯三个概念并排比较 🎯
A list sits only in the blue ring; a file object, map() and zip() sit in the green ring; a yield function or generator expression sits in the amber core.
List सिर्फ़ नीले घेरे में है; file object, map() और zip() हरे घेरे में; yield function या generator expression नारंगी केंद्र में।
列表只在蓝色圈里;文件对象、map() 和 zip() 在绿色圈里;yield 函数或生成器表达式在琥珀色核心里。
| Propertyगुण属性 | Iterable | Iterator | Generator |
|---|---|---|---|
| Hasमें है具备 | __iter__ | __iter__ + __next__ | Both (automatic, via yield)दोनों (अपने आप, yield से)两者都有(通过 yield 自动生成) |
| Built byकैसे बनता है如何创建 | A class or literal (list, str, …)Class या literal (list, str, …)类或字面量(list、str 等) | A class with __next__, or iter()__next__ वाली class, या iter()带 __next__ 的类,或 iter() | A function with yield / a generator expressionyield वाला function / generator expression带 yield 的函数 / 生成器表达式 |
| Stores values?Values रखता है?存储值吗? | YESहाँ是 (often) (अक्सर)(通常) | DEPENDSनिर्भर视情况 | NOनहीं否 — computes on the fly — चलते-चलते बनाता है — 边用边算 |
| MemoryMemory内存 | Grows with sizeSize के साथ बढ़ती है随规模增长 | Depends on its stateउसकी state पर निर्भर取决于它保存的状态 | Small and constantकम और स्थिर小且恒定 |
| Loop over it many times?कई बार loop?可多次遍历吗? | YESहाँ是 | NOनहीं否 | NOनहीं否 |
| Can be infinite?अंतहीन हो सकता है?可以是无穷的吗? | Rarelyशायद ही很少 | YESहाँ是 | YESहाँ是 (easily) (आसानी से)(很容易) |
| Lines of code to createबनाने की lines创建所需代码行数 | 1 (literal)1 (literal)1(字面量) | ~10 (class)~10 (class)约 10(类) | 2–4 (function)2–4 (function)2–4(函数) |
| Examplesउदाहरण示例 | list, dict, str, tuple, range | iter(list), a custom class, file objects, map, zipiter(list), custom class, file objects, map, zipiter(list)、自定义类、文件对象、map、zip | yield functions, generator expressionsyield functions, generator expressionsyield 函数、生成器表达式 |
The original table listed range-like objects, file objects, map and zip as generators. They are not. File objects, map and zip are iterators written in C — they have __next__ but were not made with yield. And range is an iterable, not an iterator: next(range(5)) raises TypeError, and you can loop over the same range many times.
मूल table में range जैसे objects, file objects, map और zip को generators बताया गया था। वे generators नहीं हैं। File objects, map और zip C में लिखे iterators हैं — उनमें __next__ है पर वे yield से नहीं बने। और range एक iterable है, iterator नहीं: next(range(5)) TypeError देता है, और आप एक ही range पर कई बार loop चला सकते हैं।
原表格把类似 range 的对象、文件对象、map 和 zip 列为 生成器。它们并不是。文件对象、map 和 zip 是用 C 写的 迭代器 — 有 __next__,但不是用 yield 构建的。而 range 是 可迭代对象,不是迭代器:next(range(5)) 会抛出 TypeError,并且同一个 range 可以遍历多次。
Every generator is an iterator. Every iterator is an iterable. But an iterable is not always an iterator, and an iterator is not always a generator. If you remember one sentence about these three, remember that one.
हर generator एक iterator है। हर iterator एक iterable है। पर हर iterable iterator नहीं होता, और हर iterator generator नहीं होता। इन तीनों के बारे में एक वाक्य याद रखना हो, तो यही रखिए।
每个 生成器 都是 迭代器。每个 迭代器 都是 可迭代对象。但 可迭代对象 不一定是迭代器,迭代器 也不一定是生成器。关于这三者如果只记一句话,就记这一句。
Common Pitfalls (and Good Habits) 🩹आम ग़लतियाँ (और अच्छी आदतें) 🩹常见陷阱(与好习惯)🩹
for loop it is empty, so a second loop gets zero items. Use list() if you need to loop more than once.for loop के बाद वह ख़ाली है, इसलिए दूसरे loop को शून्य items मिलते हैं। एक से ज़्यादा बार loop चाहिए तो list() लें।for 循环之后它就空了,第二次循环一个元素都拿不到。需要多次遍历就用 list()。.send(x) on a new generator, call next() once to run it to the first yield. Otherwise you get a TypeError..send(x) से पहले एक बार next() बुलाइए ताकि वह पहले yield तक चले। वरना TypeError आता है।.send(x) 之前,先调用一次 next() 让它运行到第一个 yield。否则会得到 TypeError。return value is stored in StopIteration.value — it is NOT one of the yielded items. Beginners expect it in the loop; it never appears there.return value StopIteration.value में रखी जाती है — यह yield किए गए items में से नहीं है। Beginners इसे loop में चाहते हैं; यह वहाँ कभी नहीं आती।return value 会保存到 StopIteration.value 里 — 它不是被 yield 出来的元素。初学者以为它会出现在循环里,但它永远不会。sum(x*x for x in nums) uses far less memory than sum([x*x for x in nums]). Drop the brackets whenever you feed straight into sum, max, any, all or join.sum(x*x for x in nums), sum([x*x for x in nums]) से बहुत कम memory लेता है। जब भी सीधे sum, max, any, all या join में दें, brackets हटा दीजिए।sum(x*x for x in nums) 比 sum([x*x for x in nums]) 省得多的内存。只要直接传给 sum、max、any、all 或 join,就去掉方括号。🩺 Simulator: Bug Doctor🩺 Simulator: Bug Doctor🩺 模拟器:Bug 医生
Five real mistakes with iterators and generators. Buggy code on the left, fixed code on the right, changed lines marked.
Iterators और generators की पाँच असली ग़लतियाँ। बाईं ओर buggy code, दाईं ओर ठीक किया हुआ, बदली lines चिह्नित।
五个与迭代器和生成器有关的真实错误。左边是有 bug 的代码,右边是修正后的代码,改动的行已标出。
Real-World Case Study — Streaming CSV Totals 🌍Real-World Case Study — Streaming CSV Totals 🌍真实案例 — 流式 CSV 汇总 🌍
Suppose you have a 5 GB sales CSV with 200 million rows, and you need total revenue per country. Loading it all at once would exhaust your RAM. A generator pipeline handles it with one row in flight at a time. Here the same code runs on a small sample, sales_2026.csv:
मान लीजिए आपके पास 200 million rows वाली 5 GB sales CSV है, और आपको हर देश की कुल कमाई चाहिए। सब एक साथ load करने पर RAM ख़त्म हो जाएगी। Generator pipeline एक बार में एक row चलाकर इसे सँभाल लेती है। यहाँ वही code एक छोटे sample sales_2026.csv पर चलता है:
假设你有一个 5 GB、2 亿行的销售 CSV,需要按国家汇总收入。一次性全部加载会耗尽内存。生成器流水线一次只处理一行就能搞定。这里同样的代码在一个小样本 sales_2026.csv 上运行:
import csv
from collections import defaultdict
def read_rows(path):
"""Stage 1 - yield each row as a dict, one at a time."""
with open(path, newline='') as f:
yield from csv.DictReader(f)
def only_2026(rows):
"""Stage 2 - keep 2026 sales only."""
for row in rows:
if row['date'].startswith('2026'):
yield row
def parse_amount(rows):
"""Stage 3 - turn the amount text into a number."""
for row in rows:
row['amount'] = float(row['amount'])
yield row
# Build the pipeline - nothing runs yet
pipeline = parse_amount(only_2026(read_rows('sales_2026.csv')))
# Only NOW do rows flow through - one at a time
totals = defaultdict(float)
for row in pipeline:
totals[row['country']] += row['amount']
for country, revenue in sorted(totals.items(), key=lambda x: -x[1])[:5]:
print(f"{country:20s} ${revenue:,.2f}")
The original output showed made-up totals for a 5 GB file that does not exist, and its last line ($ 94,672,110.44) could not come from this format string — ${revenue:,.2f} never adds spaces after the $. The output above is a real run on the sample file. The code is unchanged and works the same way on a 5 GB file.
मूल output में ऐसी 5 GB file के बनावटी totals थे जो है ही नहीं, और उसकी आख़िरी line ($ 94,672,110.44) इस format string से आ ही नहीं सकती — ${revenue:,.2f} $ के बाद कभी spaces नहीं जोड़ता। ऊपर का output sample file पर असली run है। Code नहीं बदला और 5 GB file पर भी ऐसे ही चलता है।
原输出给出的是一个并不存在的 5 GB 文件的虚构汇总,而且最后一行($ 94,672,110.44)不可能由这个格式字符串产生 — ${revenue:,.2f} 从不会在 $ 后加空格。上面的输出是在样本文件上真实运行的结果。代码没有改动,对 5 GB 的文件也同样适用。
🚰 Simulator: Pipeline Flow🚰 Simulator: Pipeline Flow🚰 模拟器:流水线演示
Pull one row at a time through the three generator stages into totals. 2025 rows are dropped at only_2026() and never reach parse_amount(). Only one row is ever in flight.
तीन generator stages से totals तक एक-एक row खींचिए। 2025 वाली rows only_2026() पर रुक जाती हैं और parse_amount() तक कभी नहीं पहुँचतीं। एक समय में सिर्फ़ एक row चलती है।
一次拉一行,让它经过三个生成器阶段进入 totals。2025 年的行会在 only_2026() 被丢弃,永远到不了 parse_amount()。任何时候都只有一行在流动。
read_rows()yield each rowहर row yield逐行 yieldonly_2026()drop other yearsदूसरे साल हटाएँ丢弃其他年份parse_amount()text → floattext → float文本 → floattotalsadd per countryदेश के हिसाब से जोड़按国家累加The pipeline never builds a list of rows. The totals dict is the only thing that grows — and it grows with the number of countries, not the number of rows. That is how generators scale to any dataset your disk can hold.
Pipeline कभी rows की list नहीं बनाती। सिर्फ़ totals dict बढ़ती है — और वह देशों की गिनती से बढ़ती है, rows की गिनती से नहीं। इसी तरह generators उतने बड़े data तक चल जाते हैं जितना आपकी disk रख सके।
流水线从不构建行的列表。唯一会增长的是 totals 字典 — 而它随国家数量增长,而不是随行数增长。这就是生成器能扩展到磁盘容纳得下的任何数据集的原因。
Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆
iter(). An iterator gives values on demand through next(). A generator is an iterator built with yield. If you can say this in one breath, you understand the model.iter() से iterator बनाया जा सकता है। Iterator next() से माँगने पर values देता है। Generator yield से बना iterator है। अगर आप यह एक साँस में कह सकें, तो आप पूरा model समझ गए।iter() 变成迭代器。迭代器 通过 next() 按需给出值。生成器 是用 yield 构建的迭代器。能一口气说出这句话,你就理解了整个模型。sum, max, min, any, all or str.join. Drop the square brackets — it uses far less memory.sum, max, min, any, all या str.join में जाए, तो list comprehensions की जगह generator expressions लें। Square brackets हटाइए — बहुत कम memory लगती है।sum、max、min、any、all 或 str.join 时,优先用 生成器表达式而不是列表推导式。去掉方括号 — 内存占用少得多。list().list() से नतीजे रख लें।list() 把结果保存下来。yield from to hand over to sub-generators. It reads better than a manual loop and correctly passes send(), throw() and return values through.yield from लें। यह हाथ के loop से ज़्यादा साफ़ पढ़ा जाता है और send(), throw() और return values सही तरह आगे पहुँचाता है।yield from 委托给子生成器。它比手写循环更易读,并能正确转发 send()、throw() 和 return 的值。next(my_list). Lists are iterables, not iterators. Use next(iter(my_list)) if you really need an iterator step — otherwise just use my_list[0].next(my_list) कभी न लिखें। Lists iterables हैं, iterators नहीं। अगर सच में iterator वाला क़दम चाहिए तो next(iter(my_list)) लें — वरना बस my_list[0]।next(my_list)。列表是可迭代对象,不是迭代器。真需要迭代器操作就用 next(iter(my_list)) — 否则直接用 my_list[0]。return, Python raises StopIteration for you. If a StopIteration escapes from inside a generator, Python 3.7+ turns it into a RuntimeError — so end a generator with return, never by raising StopIteration.return तक पहुँचता है, Python ख़ुद StopIteration raise करता है। अगर generator के अंदर से StopIteration बाहर निकले, तो Python 3.7+ उसे RuntimeError बना देता है — इसलिए generator को return से ख़त्म करें, StopIteration raise करके कभी नहीं।return 时,Python 会替你抛出 StopIteration。如果 StopIteration 从生成器内部泄漏出来,Python 3.7+ 会把它变成 RuntimeError — 所以要用 return 结束生成器,绝不要抛出 StopIteration。Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题
An iterable can hand out iterators (iter()); an iterator hands out values one by one (next()) until StopIteration; a generator is the shortest way to write an iterator — a function with yield that pauses and resumes with its locals intact. Generators are lazy and single-use, so they keep memory tiny for huge or infinite data and connect into pipelines.
Iterable iterators दे सकता है (iter()); iterator एक-एक करके values देता है (next()) जब तक StopIteration न आए; generator iterator लिखने का सबसे छोटा तरीक़ा है — yield वाला function जो रुकता है और अपने locals के साथ फिर चलता है। Generators lazy और एक बार इस्तेमाल वाले हैं, इसलिए बहुत बड़े या अंतहीन data पर memory बहुत कम रखते हैं और pipelines में जुड़ते हैं।
可迭代对象能交出迭代器(iter());迭代器一个接一个地交出值(next()),直到 StopIteration;生成器是写迭代器最简短的方式 — 一个带 yield 的函数,能暂停并在保留局部变量的情况下恢复。生成器是惰性且一次性的,所以面对巨大或无穷的数据时内存占用极小,还能串成流水线。