迭代器机制

在 Python 中,iter() 是一个内置函数,用来从“可迭代对象”中创建一个迭代器对象

类比理解:

想象你有一本书 📖(列表 [1,2,3]

  • nums 就是整本书(可迭代对象)
  • it = iter(nums) 就像拿到一个“书签”(迭代器对象)
  • 每次 next(it) → 翻到下一页,读一个内容
  • 当读完所有页时,再 next(it) 就会报错:StopIteration

思考下面题目👇

1
2
3
4
5
6
nums = [1, 2, 3]
it = iter(nums)
print(next(it)) # 1
nums.append(4) # nums = [1, 2, 3, 4]
print(next(it)) # 2
print(next(it)) # 3

可迭代对象 vs 迭代器

概念 含义 关键方法 示例
Iterable(可迭代对象) 能够被“遍历”的对象,可以用 for __iter__() list, tuple, str, set, dict
Iterator(迭代器) 记得“当前取到哪”的对象,可用 next() __iter__() + __next__() iter(list)

🔹 类比:

  • Iterable → 书 📖
  • Iterator → 书签 🔖
  • iter() → 把书变成书签
  • next() → 翻到下一页

特殊情况

集合 set 是可迭代对象(Iterable),但不是迭代器(Iterator)

原因:不是“没有顺序”,而是,

它能被遍历,但它自己不会记得“上次取到哪了”。

进行如下验证:

1
2
3
s = {1, 2, 3}
print(hasattr(s, '__iter__')) # 能被遍历吗?
print(hasattr(s, '__next__')) # 能自己记得位置吗?

输出:

1
2
True
False

✅ 所以 setIterable(因为有 __iter__ 方法),
❌ 但不是 Iterator(因为没有 __next__ 方法)。

延伸理解

如果想让它“变成”迭代器,怎么办?

1
2
it = iter(s)
print(next(it))

这就给集合“加了书签”,it 就变成了一个迭代器对象。

总结一句:

集合是 可迭代对象,但不是 迭代器
通过 iter() 可以从集合(或列表、字符串)得到一个迭代器。

生成器机制

生成器是一种惰性计算的迭代器,它不是一次性把所有结果放进内存,而是 “要一个,算一个”

创建方式

1️⃣ 生成器表达式

1
g = (x**2 for x in range(5))

2️⃣ 生成器函数

1
2
3
def gen():
for i in range(3):
yield i

可以思考下面的例子:

1
2
3
4
5
6
7
8
9
10
def gen():
print("start")
yield 1
print("middle")
yield 2
print("end")

g = gen()
next(g) # start
next(g) # middle

📌 特点:

  • 每次 yield 暂停执行;
  • 保存局部状态;
  • 内存效率高;
  • 可以用 fornext() 遍历。

yield vs return

项目 yield return
功能 暂停执行并返回值 结束函数执行
是否保留状态 ✅ 是 ❌ 否
返回值类型 生成器对象 具体值或None
用法 让函数变成生成器函数 正常函数返回结果

例:

1
2
3
4
def f():
yield 10
yield 20
return "done"

捕获返回值:

1
2
3
4
5
6
7
g = f()
try:
next(g) # 10
next(g) # 20
next(g) # 抛 StopIteration
except StopIteration as e:
print(e.value) # done

委托生成器

yield from 可以让一个生成器把自己的部分工作“委托”给另一个生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
def subgen():
yield 1
yield 2
return "sub done"

def main_gen():
yield "start"
result = yield from subgen()
yield result
yield "end"

for x in main_gen():
print(x)

输出:

1
2
3
4
5
start
1
2
sub done
end

📌 功能总结:

  1. 转发子生成器的所有 yield
  2. 自动捕获子生成器的 return
  3. 把返回值赋给左边的变量。

💡 总结:

iter 开门,next 取值,yield 暂停,return 结局,yield from 接力。