Python Advance 📂 Advance topics · 1 of 3 265 min read

Python Iterators and Generators — Understanding yield, Lazy Evaluation, and Memory-Efficient Loops

Confused between iterable, iterator, and generator? This tutorial clears it up with a Netflix-vs-DVD analogy, then dives into how yield works step-by-step, generator expressions, memory savings of up to 400,000×, infinite Fibonacci streams, multi-stage generator pipelines for processing 20 GB files, advanced send() / close() / yield from patterns, and a final set of 7 non-negotiable golden rules for using them in production.

🌐
SectionSection第 01 节

The Story That Explains Iterators & Generators 📺Iterators और Generators समझाने वाली कहानी 📺一个讲清迭代器与生成器的故事 📺

Streaming vs the DVD BoxsetStreaming बनाम DVD Boxset在线播放 vs DVD 套装
Imagine you buy a full DVD boxset of a 100-episode series. Every disc, every episode, sitting on your shelf — all 100 present at once, filling a whole cabinet. You paid for everything up front, even the episodes you may never watch.

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.
मान लीजिए आप 100 episodes वाली series का पूरा DVD boxset ख़रीदते हैं। हर disc, हर episode, आपकी shelf पर — सारे 100 एक साथ, पूरी अलमारी भरी हुई। आपने सब कुछ पहले ही ख़रीद लिया, वे episodes भी जो शायद कभी न देखें।

अब इसकी तुलना 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 集剧集的 完整 DVD 套装。每张光盘、每一集都摆在架子上 — 100 集同时存在,占满整个柜子。你一次性为全部内容付了钱,包括那些你可能永远不会看的集数。

再对比一下 在线播放。流媒体服务不会把 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 中,可迭代对象、迭代器 和 生成器 是三个密切相关、初学者常常混淆的概念。它们都能让你遍历数据 — 但存储、计算和交出值的方式大不相同。掌握它们,你处理巨大的文件就会像处理几行数据一样轻松。

💡
The Core Insightमूल बात核心要点

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 नहीं है। इन तीन एक-के-अंदर-एक घेरों को समझना ही पूरा खेल है।

每个生成器都是迭代器。每个迭代器都是可迭代对象。反过来不成立 — 列表是可迭代对象但 不是 迭代器,用类写的迭代器也不是生成器。理解这三个嵌套的圆圈就掌握了全部要领。

▶️
This Tutorial Has Six Simulators and a Quizइस Tutorial में छह Simulators और एक Quiz है本教程有六个模拟器和一个小测验

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 运行。


SectionSection第 02 节

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 知道如何从它得到一个迭代器。

📚 What Makes Something Iterable?कोई चीज़ Iterable कैसे बनती है?什么让一个对象可迭代?
Rule 1नियम 1规则 1
The object must have an __iter__() method that returns an iterator.
Object में __iter__() method होना चाहिए जो iterator लौटाए।
对象必须有一个返回迭代器的 __iter__() 方法。
Rule 2नियम 2规则 2
A for loop and unpacking (a, b = x) call iter() behind the scenes. The in operator uses __contains__ if the object has one, and falls back to iter() if not.
for loop और unpacking (a, b = x) पर्दे के पीछे iter() बुलाते हैं। in operator object में __contains__ हो तो उसे लेता है, न हो तो iter() पर लौट आता है।
for 循环和解包(a, b = x)会在背后调用 iter()。in 运算符如果对象有 __contains__ 就用它,没有才退回使用 iter()。
Rule 3नियम 3规则 3
A list is iterable — but calling next() on it directly fails.
List iterable है — पर उस पर सीधे 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")
OUTPUT
True False TypeError: 'list' object is not an iterator 10 20 30 StopIteration - nothing left
🔧
Fixed: The Example Now Runs to the Endसुधार: उदाहरण अब अंत तक चलता है已修正:示例现在能运行到底

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()。

⚠️
The Iterable ≠ Iterator TrapIterable ≠ Iterator का जाल可迭代对象 ≠ 迭代器 的陷阱

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 都会要一个新的迭代器。


SectionSection第 03 节

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__
returns selfself लौटाता है返回 self
An iterator's __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.
Iterator का __iter__ बस object ख़ुद लौटाता है। इसी से वह for loop में चलता है — loop iter() बुलाता है, वही iterator वापस पाता है, और इस्तेमाल शुरू करता है।
迭代器的 __iter__ 只是返回对象本身。正因如此它才能用在 for 循环中 — 循环调用 iter(),拿回同一个迭代器,然后开始使用。
▶️
__next__
returns the next valueअगली value लौटाता है返回下一个值
Called by the built-in next() function. Returns the next item and moves the internal position forward. When there are no more values, it must raise StopIteration.
Built-in next() function इसे बुलाता है। अगला item लौटाता है और अंदर की position आगे बढ़ाता है। जब values ख़त्म हों, तो इसे StopIteration raise करना ही होगा।
由内置函数 next() 调用。返回下一个元素并把内部位置向前移动。没有更多值时,必须抛出 StopIteration。
🔒
Single-Useएक बार इस्तेमाल一次性
used up onceएक बार में ख़त्म用一次就耗尽
An iterator can be walked through only once. After StopIteration, it is exhausted. To go again, build a new one from the original iterable.
Iterator पर सिर्फ़ एक बार चला जा सकता है। StopIteration के बाद वह ख़त्म हो जाता है। फिर से चलाने के लिए मूल iterable से नया बनाइए।
迭代器只能遍历 一次。StopIteration 之后它就耗尽了。要再来一遍,就从原来的可迭代对象新建一个。
🔑
iter() on an Iterator Returns ItselfIterator पर iter() ख़ुद को लौटाता है对迭代器调用 iter() 返回它自己

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
OUTPUT
True False [1, 2, 3] [] [1, 2, 3]

🧪 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 对照。

iter() / next() Labiter() / next() Labiter() / next() 实验室400 sequences checked400 sequences जाँचे已核对 400 个序列
obj =obj =obj =
Run:चलाएँ:运行:
Lines you ranचलाई गई lines你运行的代码
OutputOutput输出

SectionSection第 04 节

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=' ')
OUTPUT
1 2 3 4

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 关键字。


SectionSection第 05 节

Enter Generators — Iterators With Superpowers ⚡Generators — ख़ास ताक़त वाले Iterators ⚡生成器登场 — 拥有超能力的迭代器 ⚡

The Pause ButtonPause Button暂停键
A normal function runs from top to bottom, returns a value and forgets everything. A generator function is different — when it reaches a 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.
सामान्य function ऊपर से नीचे तक चलता है, value लौटाता है और सब भूल जाता है। Generator function अलग है — 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=' ')
OUTPUT
<generator object my_range at 0x7f…> 1 2 3 4
⚡
Why Generators Are BeautifulGenerators सुंदर क्यों हैं为什么生成器如此优雅

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。


SectionSection第 06 节

How yield Actually Works — Step by Step ⏯️yield असल में कैसे काम करता है — क़दम-दर-क़दम ⏯️yield 究竟如何工作 — 逐步拆解 ⏯️

The Pause / Resume Life Cycle of a GeneratorGenerator का Pause / Resume चक्र生成器的暂停 / 恢复生命周期
Pause and resume life cycle of a generatorSTEP 1 · CALLक़दम 1 · CALL第 1 步 · 调用gen = my_range(1, 5)body does NOT runbody नहीं चलती函数体不运行STEP 2 · next()क़दम 2 · next()第 2 步 · next()next(gen)the body startsbody शुरू होती है函数体开始执行STEP 3 · YIELDक़दम 3 · YIELD第 3 步 · YIELDyield startvalue 1 handed backvalue 1 वापस दी交回值 1STEP 4 · FREEZEक़दम 4 · FREEZE第 4 步 · 冻结start = 1 savedlocals keptlocals सुरक्षित局部变量保留next call → resume right after yieldअगली call → yield के ठीक बाद से आगे下一次调用 → 从 yield 之后继续STEP 5 · WHEN THE LOOP ENDSक़दम 5 · जब LOOP ख़त्म हो第 5 步 · 循环结束时while start < end → FalsePython raises StopIteration for youPython ख़ुद StopIteration raise करता हैPython 自动抛出 StopIteration

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 后再次冻结。局部变量在调用之间保留下来。

01
Function callFunction call函数调用
You call 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,于是 一行代码都不运行,直接返回一个生成器对象。
02
First next() callपहली next() call第一次 next() 调用
The 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。
03
Second next() callदूसरी next() call第二次 next() 调用
Execution continues on the line after yield. start becomes 2. The loop goes round, hits yield again, freezes and hands back 2.
Execution yield के बाद वाली line से आगे बढ़ता है। start 2 बनता है। Loop घूमकर फिर yield पर पहुँचता है, रुकता है और 2 देता है।
执行从 yield 之后 的那一行继续。start 变成 2。循环再转一圈,又遇到 yield,冻结并交回 2。
04
Repeat until exhaustedख़त्म होने तक दोहराओ重复直到耗尽
Steps 2–3 repeat for 3 and 4. On the fifth call, start < end is False. The function ends, and Python raises StopIteration.
क़दम 2–3, 3 और 4 के लिए दोहराए जाते हैं। पाँचवीं call पर start < end False है। Function ख़त्म होता है, और Python StopIteration raise करता है।
第 2–3 步对 3 和 4 重复。第五次调用时 start < end 为 False,函数结束,Python 抛出 StopIteration。
05
The loop ends quietlyLoop चुपचाप ख़त्म循环安静地结束
The 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 Step by StepGenerator क़दम-दर-क़दम生成器逐步演示recorded with sys.settracesys.settrace से रिकॉर्ड用 sys.settrace 录制
CodeCode代码
Call stack (newest at the bottom)Call stack (सबसे नया नीचे)调用栈(最新的在最下面)
The generatorGenerator生成器
OutputOutput输出

SectionSection第 07 节

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 () लगते हैं।

就像列表推导式能简洁地创建列表,生成器表达式 能简洁地创建生成器。语法完全相同,只是把 方括号 [] 换成 圆括号 ()。

📋 List ComprehensionList Comprehension列表推导式
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हाँ是
⚡ Generator ExpressionGenerator Expression生成器表达式
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=' ')
OUTPUT
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] <generator object <genexpr> at 0x7f…> 0 1 4 9 16 25 36 49 64 81

🐢🐇 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 运行。

Lazy vs EagerLazy बनाम Eager惰性 vs 立即event order from real runsevents का क्रम असली runs से事件顺序来自真实运行
n =
Consumer:Consumer:消费者:
[ ] list — eagerlist — eager列表 — 立即
Held in memoryMemory में रखा保存在内存中
( ) generator — lazygenerator — lazy生成器 — 惰性
Held in memoryMemory में रखा保存在内存中

SectionSection第 08 节

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")
OUTPUT
List memory: 89,095,160 bytes Generator memory: 200 bytes
Memory Footprint — 10 Million IntegersMemory — 10 Million Integers内存占用 — 1000 万个整数
Memory footprint for 10 million integersLIST · real totalLIST · असली कुल列表 · 实际总量[x*2 for x ...]≈ 409 MBLIST · getsizeofLIST · getsizeof列表 · getsizeofpointer slots only89,095,160 bytes ≈ 85 MBGENERATORGENERATOR生成器(x*2 for x ...)200 bytes≈ 445,000× smaller than getsizeof(list)getsizeof(list) से ≈ 445,000 गुना छोटा比 getsizeof(list) 小约 445,000 倍

Same computation, same 10 million values — but the generator keeps only the recipe, not the results.

वही गणना, वही 10 million values — पर generator सिर्फ़ तरीक़ा रखता है, नतीजे नहीं।

同样的计算、同样的 1000 万个值 — 但生成器只保存"配方",不保存结果。

📈
About 445,000× Less — and the Real Gap Is Even Biggerलगभग 445,000 गुना कम — और असली फ़र्क़ इससे भी बड़ा约少 445,000 倍 — 而真实差距更大

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")
OUTPUT
List: 0.41s Generator: 0.29s
⏱️
Fixed: Real Timings, Smaller Sizeसुधार: असली Timings, छोटा Size已修正:真实计时,较小规模

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 万个数字真实运行的结果。在我们的机器上,每次运行生成器都更快 — 但更大、更可靠的优势在于内存。


SectionSection第 09 节

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=' ')
OUTPUT
0 1 1 2 3 5 8 13 21 34

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)
OUTPUT
[6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229]

SectionSection第 10 节

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 上运行:

app.log (sample filesample file样本文件)
2026-07-14 10:12:30 INFO Server started on port 8080 2026-07-14 10:12:33 ERROR Database connection timed out 2026-07-14 10:12:35 INFO Retrying database connection 2026-07-14 10:12:41 ERROR Retry failed: pool exhausted 2026-07-14 10:12:58 WARN Slow response from /api/cart 2026-07-14 10:13:02 ERROR Circuit breaker opened for /api/checkout 2026-07-14 10:13:40 INFO Health check OK 2026-07-14 10:14:15 ERROR Payment gateway 502 Bad Gateway 2026-07-14 10:15:00 ERROR Session store unreachable 2026-07-14 10:15:30 ERROR Cache warm-up failed 2026-07-14 10:16:02 INFO Shutting down
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)
OUTPUT
2026-07-14 10:12:33 ERROR Database connection timed out 2026-07-14 10:12:41 ERROR Retry failed: pool exhausted 2026-07-14 10:13:02 ERROR Circuit breaker opened for /api/checkout 2026-07-14 10:14:15 ERROR Payment gateway 502 Bad Gateway 2026-07-14 10:15:00 ERROR Session store unreachable
🔥
The Generator Pipeline PatternGenerator Pipeline Pattern生成器流水线模式

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 节会逐步演示。


SectionSection第 11 节

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 ले भी सकते हैं।

生成器不仅能产生值,还能 接收 值。

⚖️ Advanced Generator MethodsAdvanced Generator Methods生成器的高级方法
.send(x)
Resumes the generator, and x becomes the value of the paused yield expression. It lets you feed data into a generator.
Generator को आगे बढ़ाता है, और x रुके हुए yield expression की value बन जाता है। इससे आप generator में data डाल सकते हैं।
恢复生成器的执行,x 成为暂停处 yield 表达式的值。它让你能把数据 送进 生成器。
.close()
Stops the generator by raising GeneratorExit inside it. Any finally block gets a chance to clean up.
Generator के अंदर GeneratorExit raise करके उसे रोकता है। कोई finally block हो तो उसे साफ़-सफ़ाई का मौक़ा मिलता है।
在生成器内部抛出 GeneratorExit 来停止它。任何 finally 块都有机会做清理。
.throw(E)
Raises exception E inside the generator, at the paused yield. Useful for signalling errors from outside.
Generator के अंदर, रुके हुए yield पर exception E raise करता है। बाहर से errors बताने में काम आता है।
在生成器内部暂停的 yield 处抛出异常 E。适合从外部发出错误信号。
yield from
Hands iteration over to another iterable or generator. Shorter than a manual for … yield loop, and it passes send/throw/return through.
Iteration किसी दूसरे iterable या generator को सौंप देता है। हाथ से लिखे 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()))
OUTPUT
Got: hello Got: 42 [3, 2, 1, 'go!', 'done']

🎛️ 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 对照。

send() Labsend() Labsend() 实验室300 sequences checked300 sequences जाँचे已核对 300 个序列
Run:चलाएँ:运行:
Generator is:Generator है:生成器状态:
The generatorGenerator生成器
Lines you ranचलाई गई lines你运行的代码
OutputOutput输出

SectionSection第 12 节

The Three Ideas Side by Side 🎯तीनों विचार साथ-साथ 🎯三个概念并排比较 🎯

The Nested-Circle Ruleएक-के-अंदर-एक घेरों का नियम嵌套圆圈法则
Nested circles: iterable, iterator, generatorITERABLEhas __iter__()में __iter__() है有 __iter__()usable in a for loopfor loop में चलता है可用于 for 循环[1, 2, 3]"hello"{"a": 1}range(5)ITERATOR__iter__ + __next__stateful, single usestate वाला, एक बार有状态,一次性iter([1, 2, 3])MyRange(1, 5)open("app.log")map(), zip()GENERATORbuilt with yieldyield से बना用 yield 构建my_range(1, 5)(x*2 for x in r)every generator ⊂ every iterator ⊂ every iterableहर generator ⊂ हर iterator ⊂ हर iterable生成器 ⊂ 迭代器 ⊂ 可迭代对象

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गुण属性IterableIteratorGenerator
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, rangeiter(list), a custom class, file objects, map, zipiter(list), custom class, file objects, map, zipiter(list)、自定义类、文件对象、map、zipyield functions, generator expressionsyield functions, generator expressionsyield 函数、生成器表达式
🔧
Fixed: Which Examples Belong Whereसुधार: कौन-सा उदाहरण कहाँ已修正:示例的归属

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 可以遍历多次。

🌱
The Nested-Circle Ruleएक-के-अंदर-एक घेरों का नियम嵌套圆圈法则

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 नहीं होता। इन तीनों के बारे में एक वाक्य याद रखना हो, तो यही रखिए।

每个 生成器 都是 迭代器。每个 迭代器 都是 可迭代对象。但 可迭代对象 不一定是迭代器,迭代器 也不一定是生成器。关于这三者如果只记一句话,就记这一句。


SectionSection第 13 节

Common Pitfalls (and Good Habits) 🩹आम ग़लतियाँ (और अच्छी आदतें) 🩹常见陷阱(与好习惯)🩹

❌
Looping Twiceदो बार Loop遍历两次
used-up generatorख़त्म हुआ generator生成器已耗尽
A generator is single-use. After one for loop it is empty, so a second loop gets zero items. Use list() if you need to loop more than once.
Generator एक बार इस्तेमाल होता है। एक for loop के बाद वह ख़ाली है, इसलिए दूसरे loop को शून्य items मिलते हैं। एक से ज़्यादा बार loop चाहिए तो list() लें।
生成器只能用一次。一次 for 循环之后它就空了,第二次循环一个元素都拿不到。需要多次遍历就用 list()。
❌
Forgetting to PrimePrime करना भूलना忘记预激
TypeError on sendsend पर TypeErrorsend 时报 TypeError
Before calling .send(x) on a new generator, call next() once to run it to the first yield. Otherwise you get a TypeError.
नए generator पर .send(x) से पहले एक बार next() बुलाइए ताकि वह पहले yield तक चले। वरना TypeError आता है।
在新生成器上调用 .send(x) 之前,先调用一次 next() 让它运行到第一个 yield。否则会得到 TypeError。
❌
Using return With a ValueValue के साथ returnreturn 带值
return ≠ yieldreturn ≠ yieldreturn ≠ yield
In a generator, 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.
Generator में return value StopIteration.value में रखी जाती है — यह yield किए गए items में से नहीं है। Beginners इसे loop में चाहते हैं; यह वहाँ कभी नहीं आती।
在生成器中,return value 会保存到 StopIteration.value 里 — 它不是被 yield 出来的元素。初学者以为它会出现在循环里,但它永远不会。
✅
Use for I/O StreamsI/O Streams के लिए用于 I/O 流
large files, streamingबड़ी files, streaming大文件、流式数据
Reading files, network data or database rows? Generators are the right tool. They pull one piece at a time, so the producer never runs far ahead of the consumer.
Files, network data या database rows पढ़ रहे हैं? Generators सही औज़ार हैं। वे एक बार में एक टुकड़ा खींचते हैं, इसलिए producer consumer से बहुत आगे नहीं भागता।
读取文件、网络数据或数据库记录?生成器是正确的工具。它们一次只拉取一份,所以生产者永远不会远远跑在消费者前面。
✅
Chain Them Into PipelinesPipelines में जोड़ें串成流水线
pipeline patternpipeline pattern流水线模式
Read → filter → transform → total — each stage a generator. Nothing is built in bulk until you consume the final result. Memory stays flat whatever the data size.
Read → filter → transform → total — हर stage एक generator। अंतिम नतीजा इस्तेमाल करने तक कुछ भी एक साथ नहीं बनता। Data कितना भी हो, memory स्थिर रहती है।
读取 → 过滤 → 转换 → 汇总 — 每一级都是生成器。在你消费最终结果之前,什么都不会被批量生成。无论数据多大,内存都保持平稳。
✅
Use genexprs in AggregationsAggregations में genexprs聚合时用生成器表达式
drop the [][] हटाइए去掉 []
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 的代码,右边是修正后的代码,改动的行已标出。

Bug DoctorBug DoctorBug 医生5 bugs · real outputs5 bugs · असली outputs5 个 bug · 真实输出
❌ BuggyBuggy有 bug
✅ Fixedठीक किया已修正

SectionSection第 14 节

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 上运行:

sales_2026.csv (sample filesample file样本文件)
date,country,amount 2026-01-03,India,1200.50 2025-12-30,Germany,999.00 2026-01-05,United States,4300.00 2026-01-07,Germany,1550.25 2026-02-11,United Kingdom,2100.00 2025-11-02,United States,700.00 2026-02-14,Japan,880.75 2026-03-01,India,2450.00 2026-03-09,United States,3150.40 2026-03-15,United Kingdom,640.10 2026-03-22,Brazil,410.00
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}")
OUTPUT
United States $7,450.40 India $3,650.50 United Kingdom $2,740.10 Germany $1,550.25 Japan $880.75
🔧
Fixed: Real Output From a Real Fileसुधार: असली File से असली Output已修正:来自真实文件的真实输出

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()。任何时候都只有一行在流动。

Pipeline FlowPipeline Flow流水线演示one row in flightएक बार में एक row一次一行
read_rows()yield each rowहर row yield逐行 yield
only_2026()drop other yearsदूसरे साल हटाएँ丢弃其他年份
parse_amount()text → floattext → float文本 → float
totalsadd per countryदेश के हिसाब से जोड़按国家累加
sales_2026.csv
The only thing that growsसिर्फ़ यही बढ़ता है唯一会增长的东西
🏆
200 Million Rows, One Row at a Time200 Million Rows, एक बार में एक Row2 亿行,一次一行

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 字典 — 而它随国家数量增长,而不是随行数增长。这就是生成器能扩展到磁盘容纳得下的任何数据集的原因。


SectionSection第 15 节

Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆

🌲 Iterators & Generators — Rules to RememberIterators और Generators — याद रखने वाले नियम迭代器与生成器 — 必须记住的规则
1
An iterable can be turned into an iterator with 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.
Iterable को iter() से iterator बनाया जा सकता है। Iterator next() से माँगने पर values देता है। Generator yield से बना iterator है। अगर आप यह एक साँस में कह सकें, तो आप पूरा model समझ गए।
可迭代对象 可以用 iter() 变成迭代器。迭代器 通过 next() 按需给出值。生成器 是用 yield 构建的迭代器。能一口气说出这句话,你就理解了整个模型。
2
Use generators for large or infinite data. If the data does not fit comfortably in memory, use a generator. If it is infinite (Fibonacci, a sensor stream), you have no other choice.
बड़े या अंतहीन data के लिए generators लें। अगर data आराम से memory में न समाए, तो generator लें। अगर अंतहीन है (Fibonacci, sensor stream), तो कोई और रास्ता ही नहीं।
大数据或无穷数据用生成器。数据放不进内存时用生成器;如果数据是无穷的(斐波那契、传感器数据流),你别无选择。
3
Prefer generator expressions over list comprehensions when the result goes straight into 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 时,优先用 生成器表达式而不是列表推导式。去掉方括号 — 内存占用少得多。
4
A generator can be used only once. To loop again, call the generator function again for a fresh generator, or keep the results with list().
Generator सिर्फ़ एक बार इस्तेमाल होता है। फिर से loop के लिए generator function दोबारा बुलाकर नया generator लें, या list() से नतीजे रख लें।
生成器 只能用一次。要再遍历一次,就重新调用生成器函数得到新的生成器,或者用 list() 把结果保存下来。
5
For pipelines, use yield from to hand over to sub-generators. It reads better than a manual loop and correctly passes send(), throw() and return values through.
Pipelines में sub-generators को सौंपने के लिए yield from लें। यह हाथ के loop से ज़्यादा साफ़ पढ़ा जाता है और send(), throw() और return values सही तरह आगे पहुँचाता है।
在流水线中用 yield from 委托给子生成器。它比手写循环更易读,并能正确转发 send()、throw() 和 return 的值。
6
Never write 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]。
7
When a generator reaches the end of its body or a bare 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.
जब generator अपनी body के अंत या ख़ाली 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 道题

🏆 QuizPick an answer to see why.जवाब चुनें और कारण देखें।选一个答案,看看原因。
🎯
Key Takeawayमुख्य सीख关键要点

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 的函数,能暂停并在保留局部变量的情况下恢复。生成器是惰性且一次性的,所以面对巨大或无穷的数据时内存占用极小,还能串成流水线。

🐍 Try Python Yourself

Write, run and visualize Python step by step. No install needed.