📄 一句话概括
你每天写的 for x in ... 底下都站着一个迭代器:一个知道【下一个是什么】的对象,按需吐一个、吐完拉倒——正因为【按需】,它可以一次只生产一个数据,一亿个数字也吃不了一亿个的内存。
📝 本节和前面几节的关系
4-3 的 for 是【用法】,这一篇掀开它的【机器盖】。回收 3-8 的数组(可迭代的代表)与 3-13 的字典(键的迭代)——能被 for 遍历的东西,背后全是迭代协议。
一、可迭代对象与迭代器:两台机器
- 可迭代对象(iterable):能被 for 遍历的东西——列表、字符串、字典、文件……它的本事是【能开出一张取货单】(
iter(x)); - 迭代器(iterator):真正干活的那台机器——记住【发到哪了】,每次
next()吐下一个,吐完抛 StopIteration 报【没货了】。
nums = [10, 20, 30]
it = iter(nums) # 从可迭代对象开出迭代器
print(next(it)) # 10
print(next(it)) # 20
print(next(it)) # 30
print(next(it)) # StopIteration!——for 就是替你默默接住这个异常然后收工
for 循环的真相:for x in nums 等价于【开迭代器 → 不停 next → 接住 StopIteration 就结束】——你写了十年的 for,底层全是这一套。
二、生成器:自己造一台迭代器
生成器函数用 yield 代替 return——函数【暂停在 yield 处】,下次要值再从原地继续:
def countdown(n):
while n > 0:
yield n # 吐一个、暂停;下次 next 从这行后面继续
n -= 1
for x in countdown(3):
print(x) # 3 2 1
惰性求值的威力:数据【要一个才生产一个】:
def big():
for i in range(100_000_000): # 一亿个数
yield i
# 内存里永远只有【当前这一个】——列表版则会真存一亿个数字
适用分界:要反复访问、要切片、要知道长度——用列表;只扫一遍(读大文件逐行、过滤流水线)——用生成器,内存账省到姥姥家。
三、动手 Demo:next 现场与内存对比
def even_up_to(n):
for i in range(0, n + 1, 2):
yield i
it = even_up_to(10)
print(next(it), next(it), next(it)) # 0 2 4
print(list(it)) # [6, 8, 10] —— 从 6 接着吐到头
for x in even_up_to(8): # 同一函数可反复开出新迭代器
print(x, end=" ")
print()
看到什么算成功:0 2 4、[6, 8, 10]、0 2 4 6 8。
读结果:前三个 next 吐 0/2/4 后机器记住了位置——list(it) 从 6 接着吐,说明迭代器【只此一份、用过不回】;同一函数再次 for 又从 0 开始(新开了一台机器)。
五、30 秒实验
跑 it = iter("你好") 然后连续 next(it)——第一次得到字符【你】。字符串也是可迭代对象,逐【字符】吐(回收 3-9:Python 按字符不按字节)。再跑 next(it) 三次,看 StopIteration 长什么样。
六、常见误区
- 【列表和迭代器差不多,随便换】——差远了。列表【存着全部、可反复访问】;迭代器【边产边发、一次性】——迭代器走完就空了,
list(it)之后 it 已经是空机器。 - 【生成器是语法糖,没实际好处】——错。一亿行日志逐行处理,列表版直接把内存吃满(可能几十 GB),生成器版内存恒定几 MB——不是优化技巧,是能不能跑起来的分界。
- 【迭代器只能迭代列表这类容器】——错。文件对象、range、字典、甚至无限序列(
itertools.count())都能迭代——只要实现迭代协议。
自测三问(点开对答案)
1. **可迭代对象与迭代器的关系?** 答:可迭代对象能开出迭代器(iter);迭代器持进度、每次 next 吐一个,吐完抛 StopIteration——for 底层就是这一套。 2. **生成器为什么省内存?** 答:yield 让函数暂停续跑,数据按需生产、内存里永远只有当前一个;列表则一次性把全部装进内存。 3. **迭代器遍历完再取值会怎样?** 答:抛 StopIteration(for 内部接住即收工);手动 next 就是你亲眼看到这个异常——迭代器一次性、用过不回。- ☐ 下一节(4-10 内存泄露、垃圾回收)
相关笔记
- 循环 —— for 的底层机器
- 内存泄露与垃圾回收 —— 迭代器省内存的机制面
- C++ 13.4 · 迭代器 —— 同一概念在 C++ 的形态
- ⌨️ 编程原理 · 索引 —— 全课路线图