一、迭代器 (Iterator)
- 核心概念
可迭代对象 (Iterable):可以被for循环遍历的对象(如list/str/tuple/dict/set)
本质:内部实现了__iter__()方法
迭代器 (Iterator):用来逐个取出元素、记住遍历位置的对象
本质:同时实现__iter__() + next ()方法
iter ():返回自己
next ():返回下一个元素,没有则抛 StopIteration
特点:
只能往前,不能后退
遍历一次就耗尽
惰性取值(不一次性加载所有数据) - 关键方法
iter(可迭代对象) → 获取迭代器
next(迭代器) → 取下一个元素
遍历完会抛出:StopIteration 异常 - 极简示例
python
lst = [1, 2, 3]
it = iter(lst) # 获取迭代器
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# print(next(it)) # 报错 StopIteration
- 自定义迭代器(重点)
必须实现两个方法:
iter ():返回迭代器自身
next():返回下一个值,结束抛StopIteration
python
class MyIterator:
def __init__(self):
self.num = 1
def __iter__(self):
return self
def __next__(self):
if self.num > 3:
raise StopIteration
val = self.num
self.num += 1
return val
# 使用
for i in MyIterator():
print(i) # 1 2 3
二、生成器 (Generator)
- 核心概念
生成器 = 优雅的迭代器
一边循环一边计算,不一次性生成所有数据
优点:省内存!适合大数据 / 无限序列 - 两种创建方式
① 生成器表达式(简单)
把列表推导式的 [] 改成 () 就是生成器
python
g = (x for x in range(3))
print(next(g)) # 0
print(next(g)) # 1
② 生成器函数(重点)
使用 yield 关键字,不是 return
调用函数不执行代码,返回生成器对象
next() 时执行到 yield 暂停,返回值
再次 next() 从暂停处继续执行
python
def gen():
yield 1
yield 2
yield 3
g = gen() # 得到生成器
print(next(g)) # 1
print(next(g)) # 2
- yield 工作原理
调用函数 → 返回生成器,不运行
next() → 运行到 yield 暂停,返回值
再次 next() → 从暂停位置继续执行
函数结束 → 自动抛 StopIteration - send () 方法(进阶)
send(value):可以向生成器传值
第一次必须用 next() 或 send(None)
三、迭代器 vs 生成器 对比(必会)
| 特性 | 迭代器 | 生成器 |
|---|---|---|
| 写法 | 类实现__iter__+next | 函数 + yield / 表达式() |
| 代码量 | 多 | 极少 |
| 内存占用 | 较高 | 极低(惰性计算) |
| 功能 | 基础迭代 | 功能更强,支持协程 |
| 使用场景 | 通用迭代 | 大数据、无限序列、协程 |
一句话总结:生成器就是简化版、高性能版的迭代器。
四、常用知识点
所有生成器都是迭代器,反之不成立
for 循环本质:自动调用 iter() + next() + 捕获异常
迭代器 / 生成器只能遍历一次,用完即废
总结
可迭代对象:能for循环 → 有__iter__()
迭代器:能next() → 有__iter__()+next ()
生成器:用yield的函数 → 最推荐使用
核心优势:惰性计算、节省内存
PS
什么是协程(Coroutine)?
可以暂停、可以恢复、用户态下切换的函数 = 协程
比线程轻量得多
不需要操作系统调度
遇到阻塞(IO、sleep)可以主动让出执行权
Python 中生成器是实现协程的基础
现代 Python 协程用 async/await,底层仍然基于生成器机制
简单理解:
函数可以暂停 → 去做别的事 → 再回来继续执行,这就是协程。
yield 关键字作用
核心作用 4 点:
- 让函数变成生成器
函数里只要有 yield,调用它不再直接运行,而是返回生成器对象。 - 暂停函数执行
执行到 yield 就停住,保存当前状态。 - 返回一个值
yield x 会把 x 返回给调用者(next() 拿到)。 - 可以接收外部传入的值
通过 send() 可以把值传给 yield,实现双向通信。
极简记忆:
yield = 暂停 + 返回值 + 可恢复
生成器为什么节省内存?
因为它是惰性计算(懒加载):
列表:一次性把所有元素创建好放内存
生成器:用到时才计算一个,用完就丢
不保存整个序列,只保存当前状态 + 计算规则
所以:
处理百万、亿级数据时,生成器几乎不占内存。