Python 中 for 循环能够遍历列表、字符串、文件等各种对象,不是因为解释器给每种类型写了特例,而是因为这背后有一个统一的机制——迭代协议。理解这个协议,你就能看懂 for 到底在做什么,也能自己写出可迭代的自定义对象。
迭代协议
迭代协议是 Python 为“可被遍历”的对象约定的一套接口规则,由两个核心方法组成:
iter():返回一个迭代器对象。next():返回迭代器当前的值,并将内部指针移到下一个元素;如果没有元素了,则抛出StopIteration异常。
任何实现了 iter 方法的对象都是可迭代对象(iterable)。
任何同时实现了 iter 和 next 方法的对象就是迭代器(iterator)。
换句话说,可迭代对象是“能给你迭代器”的东西,而迭代器是“真正执行逐个取值任务”的东西。
可迭代对象
- 定义:实现了
iter方法,调用该方法会返回一个迭代器。 - 常见例子:列表、元组、字符串、字典、集合、文件对象、
range()对象等都属于可迭代对象。 - 关键点:可迭代对象本身不一定是迭代器,它只是可被迭代。每次对它调用
iter()(本质上调用iter)都会得到一个新的迭代器,从起始位置重新开始遍历。
实际验证:
lst = [1, 2, 3]
print(hasattr(lst, '__iter__')) # True —— 可迭代对象
print(hasattr(lst, '__next__')) # False —— 不是迭代器
迭代器
- 定义:同时实现了
iter和next的对象。其中iter通常返回self(即迭代器本身就是可迭代的),next负责依次返回元素并在结束时抛出StopIteration。 - 特点:
- 惰性求值:只在需要时才产生下一个值,不预先在内存中存储所有数据,这对于处理大文件或无限序列非常有价值。
- 单向消费:一旦用
next()取走一个值,就不能回头再取同一个值,除非重新获取一个新的迭代器。 - 获取迭代器的方式:
- 对可迭代对象使用内置函数
iter()。 - 在
for循环中,Python 自动帮你调用iter()获取迭代器,然后不断调用next(),直到捕获StopIteration。
示例:手动使用迭代器
s = "abc"
it = iter(s) # 获取迭代器
print(next(it)) # 'a'
print(next(it)) # 'b'
print(next(it)) # 'c'
# next(it) # StopIteration
for 循环的内部原理
平常我们写的:
for item in [1,2,3]:
print(item)
实际上等价于:
iterable = [1,2,3]
iterator = iter(iterable) # 调用 __iter__
while True:
try:
item = next(iterator) # 调用 __next__
except StopIteration:
break
print(item)
因此,任何满足迭代协议的对象都可以直接被 for 循环使用,这也意味着你可以在自定义类中实现 iter 和 next,让实例变得“可遍历”。
实际应用价值
- 处理大文件:直接对文件对象迭代
for line in file,按行读取而不是一次性全部载入内存。 - 生成无限序列:使用迭代器可以表示“所有偶数”这种理论上的无限集合,结合
next()逐步取值,不会撑爆内存。 - 自定义可迭代类:当你设计一个代表数据库查询结果的数据结构时,实现迭代协议后,用户就能直接用
for row in query_result的方式遍历,使用体验和标准类型完全一致。
理解迭代协议,你就从“会用 for 循环”进阶到了“可以为任意对象设计遍历方式”的水平,这也是 Python 灵活性和统一性的重要基石。