Skip to content

Python 迭代器:揭开 for 循环背后的神秘面纱

引言:水龙头 vs 水桶

家里要用水,有两种方式:

  • 水桶:一次性打满一大桶水,随用随取。简单直接,但桶再大也有限,而且抬回家很费劲;
  • 水龙头:拧开就流,关了就停。水流是"无限"的(只要供水厂不停),而且你不用提前存储。

前几篇我们学的列表、元组、字典是"水桶"——数据早就全部装好了;而**迭代器(Iterator)**是"水龙头"——数据在你拧开的那一刻才流出来,你不拧,它就不流。

这篇我们来揭开 Python for 循环的底层秘密,搞清楚 Iterable、Iterator、生成器之间到底是什么关系。


一、先复习:什么是可迭代对象(Iterable)

1.1 能放进 for 循环的都是 Iterable

我们学过,以下这些都能直接作用于 for 循环:

  • 集合数据类型listtupledictsetstr 等;
  • 生成器:生成器表达式和带 yield 的生成器函数。

这些"能放进 for 循环"的对象,统称为可迭代对象(Iterable)

用代码验证一下:

python
>>> from collections.abc import Iterable
>>> isinstance([], Iterable)               # list
True
>>> isinstance({}, Iterable)               # dict
True
>>> isinstance('abc', Iterable)            # str
True
>>> isinstance((x for x in range(10)), Iterable)   # 生成器
True
>>> isinstance(100, Iterable)              # int,不行
False

生活化理解:Iterable 就像"能放进榨汁机的水果"——苹果、橙子、西瓜都可以,但石头不行。

1.2 但 Iterable 不等于 Iterator

关键问题来了:能被 for 循环,不等于能被 next() 调用

python
>>> isinstance([], Iterator)               # list 不是迭代器
False
>>> isinstance((x for x in range(10)), Iterator)   # 生成器是迭代器
True

生活化理解:榨汁机(for 循环)可以处理水果(Iterable),但水果本身不会自己流出果汁——你得先按下开关(iter())把它变成"果汁机状态"(Iterator),然后一杯一杯接(next())。


二、什么是迭代器(Iterator)

2.1 Iterator 的定义

可以被 next() 函数调用,并不断返回下一个值的对象,称为迭代器(Iterator)

判断方法:

python
>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)   # 生成器是
True
>>> isinstance([], Iterator)                       # list 不是
False
>>> isinstance({}, Iterator)                       # dict 不是
False
>>> isinstance('abc', Iterator)                    # str 不是
False

核心结论生成器都是 Iterator,但 list/dict/str 虽然是 Iterable,却不是 Iterator

2.2 为什么 list 不是 Iterator?

因为 Iterator 表示的是一个"数据流",它的特点是:

  • next() 调用时,现算下一个值;
  • 惰性计算:不算就不知道下一个值是什么;
  • 可以无限长:比如全体自然数,理论上可以一直 next() 下去。

list 是一个已经存在的完整序列——所有元素都在内存里,可以直接 L[0]L[1] 访问,不需要"现算"。

生活化理解

  • list 像一本已经印好的书,你可以直接翻到第 5 页;
  • Iterator 像现场说书,你喊一声"下一段",他说一段,不喊他就不说,也不知道他总共要说多少段。

2.3 Iterator 可以表示无限数据流

python
>>> def infinite():
...     n = 0
...     while True:
...         yield n
...         n += 1
...
>>> g = infinite()
>>> isinstance(g, Iterator)
True
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
# ... 可以永远调用下去,不会内存爆炸

list 存储"全体自然数"是不可能的,但 Iterator 可以——因为它不存储,只计算


三、iter() 和 next():Iterable 与 Iterator 的桥梁

3.1 把 Iterable 变成 Iterator

python
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

iter() 函数可以把一个 Iterable 变成 Iterator。

生活化理解iter() 就像把水果放进榨汁机并按下开关——水果(list)变成了果汁机(Iterator),现在可以一杯一杯接了。

3.2 手动体验:iter() + next()

python
>>> L = [1, 2, 3]
>>> it = iter(L)       # 把 list 变成 Iterator
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
  ...
StopIteration

next() 就像"接一杯果汁",接完一杯还有,直到接完最后一杯,再按就报错 StopIteration(没果汁了)。


四、for 循环的底层真相:iter() + next() + try...except

这是最震撼的部分:Python 的 for 循环,本质上就是 iter() + next() 的循环调用

4.1 你以为的 for 循环

python
for x in [1, 2, 3, 4, 5]:
    pass

4.2 实际发生的完整过程

python
# 第 1 步:拿到 Iterator 对象
it = iter([1, 2, 3, 4, 5])

# 第 2 步:循环调用 next()
while True:
    try:
        x = next(it)     # 取下一个值
    except StopIteration:
        break            # 没有更多值,退出循环

生活化理解:for 循环就像一个自动榨汁机器人——它先把水果放进榨汁机(iter()),然后不停地按"出汁"按钮(next()),直到机器说"没了"(StopIteration)才停。

4.3 为什么要有这层封装?

因为统一接口!不管你是 list、str、dict、生成器,还是自定义对象,只要你能变成 Iterator,for 循环就能用同一套流程处理你。

这就像 USB 接口——不管你是鼠标、键盘、U盘还是打印机,只要支持 USB 协议,电脑就能识别。


五、Iterable vs Iterator:一张表看懂

对比维度Iterable(可迭代对象)Iterator(迭代器)
定义能作用于 for 循环的对象能被 next() 调用的对象
判断isinstance(x, Iterable)isinstance(x, Iterator)
例子list、tuple、dict、str、set生成器、iter() 的返回值
能否被 next()不能
能否被 for
数据存储数据已完整存在数据是"现算"的
能否无限长不能(内存装不下)能(如无限生成器)
转换关系iter() → Iterator本身就是 Iterator

记忆口诀

Iterable 是"能被 for 的",Iterator 是"能被 next 的"。生成器既是 Iterable 又是 Iterator。


六、实战应用场景

6.1 手动控制迭代节奏

python
it = iter([1, 2, 3, 4, 5])

# 只取前 3 个,剩下的留着
for _ in range(3):
    print(next(it))
# 输出:1, 2, 3

# 剩下的还在,可以继续
print(next(it))   # 4
print(next(it))   # 5

6.2 实现自定义迭代器

python
class Countdown:
    """倒计时迭代器"""
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        self.current -= 1
        return self.current + 1

# 使用
for n in Countdown(5):
    print(n)
# 输出:5, 4, 3, 2, 1

核心:实现 __iter__() 返回自身,实现 __next__() 返回下一个值(没值时抛 StopIteration)。

6.3 配合 itertools 做复杂迭代

python
from itertools import islice, cycle

# cycle:无限循环迭代一个序列
colors = cycle(['红', '绿', '蓝'])
for _ in range(7):
    print(next(colors))
# 输出:红, 绿, 蓝, 红, 绿, 蓝, 红

# islice:从迭代器中切一段
g = (x * x for x in range(100))
first_10 = list(islice(g, 10))   # 只取前 10 个,不全部加载

七、常见误区与避坑指南

误区 1:以为 Iterable 就是 Iterator

python
>>> L = [1, 2, 3]
>>> next(L)
Traceback (most recent call last):
  ...
TypeError: 'list' object is not an iterator

纠正list 是 Iterable,不是 Iterator。要用 next(),先 iter()

python
>>> it = iter(L)
>>> next(it)
1

误区 2:以为 Iterator 可以索引访问

python
>>> g = (x for x in range(5))
>>> g[0]
Traceback (most recent call last):
  ...
TypeError: 'generator' object is not subscriptable

纠正:Iterator 是"数据流",没有索引概念。要取第 n 个,只能 next() n 次,或者用 islice

误区 3:在 for 循环中再次 iter() 导致逻辑错误

python
L = [1, 2, 3]
for x in L:
    it = iter(L)   # 每次循环都重新创建迭代器
    print(next(it))
# 输出:1, 1, 1 —— 每次都在取第一个!

纠正:for 循环自己会调 iter(),不要手动再调一次。

误区 4:以为 StopIteration 是错误

python
it = iter([1])
next(it)   # 1
next(it)   # StopIteration

纠正StopIteration 不是 bug,是迭代器说"我完了"的正常信号。for 循环内部靠捕获它来决定何时退出。

误区 5:混淆生成器、迭代器、可迭代对象

终极判断法

python
from collections.abc import Iterable, Iterator

def check(obj):
    print(f"是 Iterable? {isinstance(obj, Iterable)}")
    print(f"是 Iterator? {isinstance(obj, Iterator)}")

check([1, 2, 3])                    # Iterable: True,  Iterator: False
check(iter([1, 2, 3]))              # Iterable: True,  Iterator: True
check(x for x in range(3))          # Iterable: True,  Iterator: True

关系总结:所有 Iterator 都是 Iterable(能被 next 的必然能被 for),但不是所有 Iterable 都是 Iterator。


八、动手练习

  1. 基础题:写一个函数 my_enumerate(iterable, start=0),不用内置 enumerate,用 iter()next() 实现相同功能。
  2. 进阶题:写一个迭代器类 Cycle,接收一个列表,无限循环迭代它(如 [1, 2, 3] → 1, 2, 3, 1, 2, 3, ...)。
  3. 挑战题:用 iter() + next() 手动模拟 for x in [1, 2, 3]: print(x) 的完整过程,包括 StopIteration 的处理。
  4. 思考题for x in [1, 2, 3]for x in iter([1, 2, 3]) 有区别吗?Python 内部会怎么处理?

小结

  • Iterable(可迭代对象):能作用于 for 循环的对象,如 list、dict、str、生成器。
  • Iterator(迭代器):能被 next() 调用的对象,表示一个惰性计算的数据流,可以无限长。
  • 生成器都是 Iterator,但 list/dict/str 只是 Iterable,不是 Iterator。
  • iter():把 Iterable 变成 Iterator;next():取出下一个值,没值时抛 StopIteration
  • for 循环的本质iter() 拿到 Iterator,然后循环 next(),捕获 StopIteration 后退出。
  • 核心心法:Iterator 是"水龙头"哲学——数据不是存好的,而是现算现给的。理解这一点,你就理解了 Python 迭代体系的底层逻辑。