我从一个真实场景说起。我刚进团队那年接手一个数据处理模块,负责遍历一批订单记录,按条件筛选、聚合、导出。第一版我全用 for + 下标写,自认为很顺手,直到数据源从本地数组换成数据库游标,从几千条变成几百万条,麻烦陆续冒出来。回头去看,如果我一开始就把"迭代(Iterator)"当成一条独立设计主线来想,很多后来补的兼容逻辑都不用写。迭代器不是一个炫技的语法点,它解决的是遍历这个动作如何与数据结构解耦,如何按需取数据,如何表达一个会持续产生数据的流。无论你写 Python、Java、JavaScript 还是 C++,这个抽象都会以不同面目出现在你面前。本文适合刚开始接触编程、被各种 Iterable、Iterator、Generator 名词绕晕的新人,也适合写了一些代码但总在大文件处理、分页拉取、自定义遍历场景里反复踩坑的开发者。
1. 先把概念说透:迭代和循环到底差在哪
很多教程会把 for 循环直接等同于迭代,看起来没毛病,但一旦深入就解释不了很多现象。比如同一个 for 循环跑过一遍之后,为什么再跑一遍就空了呢?为什么 range 对象能复用、文件对象第一次遍历完再遍历就拿到空内容?这些现象背后,是循环和迭代这两个层次不同的概念在起作用。
1.1 for 循环底下发生的事
循环是一个语句结构,它描述的是"重复执行某段逻辑"。而迭代描述的是"按照顺序逐个获取元素并处理"这一整套能力。语言为了把后者统一起来,设计了一套协议:可迭代对象、迭代器和迭代过程。
我们用 Python 看一个最普通的列表:
python复制nums = [2, 3, 5, 7]
# 第一步:拿到一个迭代器
it = iter(nums)
print(it) # <list_iterator object at 0x...>
# 第二步:反复调用 next 获取下一个元素
print(next(it)) # 2
print(next(it)) # 3
# 第三步:写到没有元素时,迭代器抛出 StopIteration
print(next(it)) # 5
print(next(it)) # 7
print(next(it)) # 抛 StopIteration
这段代码把 for 循环拆开了。列表本身不是迭代器,它是一个可迭代对象;通过 iter() 可以从它身上拿到独立的一个迭代器,迭代器记录着"当前遍历到哪里"。list 可以被重复 iter,因为每次 iter(nums) 都会生成一个全新的迭代器,从头开始;而一个已经生成的迭代器 it 是有状态的,内部指针在往前走,走到底再调用 next,就只能抛异常。
for 循环不过是把这件事包成了语法糖:
python复制for x in nums:
print(x)
解释器执行的时候,先调用 iter(nums) 拿到迭代器,然后不停调用 next(it),捕获到 StopIteration 就退出循环。这是所有支持迭代协议的容器遍历方式。写出这样一层语义之后,语言才能让不同类型的数据结构拥有同一种遍历姿势。
1.2 为什么不直接给一个数组,非要抽象出一个迭代器
如果数据全都装在内存数组里,迭代器的价值还没有那么明显。但实际开发中数据来源五花八门:有的在链表中,不存在下标,只能从某个节点 next 一路找下去;有的在远程接口里,每批只返回一页;有的是一个传感器数据流,永远没有终点;还有的是一个几百 GB 的日志文件,根本没有办法一次性读入内存变成数组。
如果把遍历能力和数据结构绑定,那么针对每种结构都要单独写一套遍历逻辑。迭代器做的事是提炼一个公约数:你能提供"下一个元素是什么"的方法,我就能用同一种 for 语法消费你。消费者不需要关心底层是数组、链表、文件还是网络请求,这是解耦的第一步。
另一个关键点是惰性获取。数组是木已成舟,把全部数据都放在那里等着被取;迭代器更像一个自助餐传送带,你不伸手,食物一直在厨房里备着,不会全部堆到你面前。取到第几个停止,成本和前面已经取过的数量有关,和未取到的总量没太大关系。这个特性在处理大数据时几乎决定程序能不能跑起来,后面我用文件和大分页接口具体展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 各语言里的 Iterator 协议:同一件事的不同表达
迭代器不是某个语言的专用概念,但每个语言落地的方式不完全一样。理解共通点之后,再看各自的语法细节会快很多。抽象上它们都在表达"如何生产下一个元素、如何表示结束"两件事。
2.1 Python 的迭代器协议
Python 是一个靠接口约定来工作的语言。要让一个对象能 for 遍历,通常得实现迭代器协议。
一个对象算"可迭代对象",最简单的判断是 iter() 对它能不能生效。在类里实现 __iter__ 并返回一个迭代器,这个对象就是可迭代的。迭代器要实现 __next__ 方法,每次调用返回下一个值,没有值时抛 StopIteration。很多迭代器同时实现了 __iter__ 和 __next__,也就是说它自己返回自己,这种写法最常见,尤其在做状态机类的遍历时。
python复制class CountDown:
def __init__(self, n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n <= 0:
raise StopIteration
current = self.n
self.n -= 1
return current
for x in CountDown(3):
p
