Python 生成器与迭代器详解
迭代器(Iterator)和生成器(Generator)是 Python 中处理可迭代对象的核心机制 ,核心价值是惰性计算 :用到数据时才生成,不占用大量内存,特别适合处理大数据、无限序列等场景。
先理清核心关系: 可迭代对象 → 迭代器 → 生成器
- 生成器是特殊的迭代器;
- 迭代器是可迭代对象的一种实现。
一、基础概念
1. 可迭代对象(Iterable)
所有能被 for 循环遍历的对象都是可迭代对象,比如:列表、字符串、元组、字典、集合等。 判断标准:对象实现了 __iter__() 方法,调用后能返回一个迭代器。
python
# 列表是可迭代对象
lst = [1, 2, 3]
print(hasattr(lst, '__iter__')) # True
2. 迭代器(Iterator)
迭代器是实现了迭代协议的对象,必须同时实现两个方法:
__iter__():返回迭代器自身__next__():返回下一个数据,无数据时抛出StopIteration异常
迭代器的特点:
- 惰性计算:不一次性生成所有数据,节省内存
- 一次性使用:遍历完就失效,无法重复遍历
- 只能向后遍历,不能回退
二、迭代器的手动实现与使用
1. 把可迭代对象转为迭代器
用 iter() 函数(内置函数,本质调用 __iter__()):
python
lst = [1, 2, 3]
# 转为迭代器
it = iter(lst)
# 手动获取下一个元素(调用 __next__())
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# print(next(it)) # 抛出 StopIteration 异常
2. for 循环的本质
for 循环会自动调用 iter() 和 next(),并自动捕获异常:
python
# 底层逻辑
it = iter(lst)
while True:
try:
x = next(it)
print(x)
except StopIteration:
break
3. 自定义迭代器类
通过类实现迭代协议,演示一个生成 1~n 整数的迭代器:
python
class MyIterator:
def __init__(self, end):
self.end = end
self.current = 1 # 记录当前位置
# 必须实现:返回迭代器自身
def __iter__(self):
return self
# 必须实现:返回下一个值
def __next__(self):
if self.current > self.end:
raise StopIteration # 无数据时抛出异常
value = self.current
self.current += 1
return value
# 使用自定义迭代器
it = MyIterator(3)
for num in it:
print(num) # 输出 1,2,3
三、生成器(Generator)
生成器是简化版的迭代器 ,无需手动写类、实现 __iter__ 和 __next__,Python 自动帮我们完成。
生成器有两种写法:
- 生成器函数 (带
yield关键字) - 生成器表达式(简洁语法)
1. 生成器函数(核心)
普通函数用 return 返回值,生成器函数用 yield 返回值:
- 执行到
yield时,函数暂停执行,并返回当前值 - 下次调用
next()时,从暂停位置继续执行 - 函数执行完毕,自动抛出
StopIteration
示例:生成 1~n 的整数
python
# 生成器函数
def my_generator(end):
current = 1
while current <= end:
yield current # 暂停并返回值,替代 return
current += 1
# 创建生成器对象
gen = my_generator(3)
# 使用方式和迭代器完全一致
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
# print(next(gen)) # StopIteration
核心优势:生成无限序列
迭代器/生成器可以轻松生成无限长度的数据,而列表做不到:
python
# 无限生成自然数
def infinite_nums():
num = 0
while True:
yield num
num += 1
gen = infinite_nums()
print(next(gen)) # 0
print(next(gen)) # 1
# 可以无限调用 next(),不会占用大量内存
2. 生成器表达式
语法和列表推导式几乎一样,只是把 [] 换成 ():
python
# 列表推导式(一次性生成所有数据,占内存)
lst = [x for x in range(3)]
print(lst) # [0,1,2]
# 生成器表达式(惰性生成)
gen = (x for x in range(3))
print(gen) # <generator object <genexpr> at 0x...>
print(next(gen)) # 0
四、生成器的高级用法
1. send() 方法:向生成器传值
send() 可以给 yield 表达式传递参数,实现双向通信:
python
def gen():
value = 0
while True:
# 接收外部 send 的值
receive = yield value
if receive:
value = receive
value += 1
g = gen()
print(next(g)) # 0 (启动生成器)
print(g.send(10))# 11 (传入10,value=10+1)
print(g.send(20))# 21
2. close() 方法:手动关闭生成器
python
g = my_generator(5)
next(g)
g.close() # 关闭生成器
# next(g) # 抛出 StopIteration
五、迭代器 vs 生成器:核心对比
| 特性 | 迭代器 | 生成器 |
|---|---|---|
| 实现方式 | 类 + __iter__+__next__ |
函数 + yield / 生成器表达式 |
| 代码量 | 繁琐 | 极简 |
| 内存占用 | 惰性计算,省内存 | 惰性计算,省内存(更简洁) |
| 功能 | 基础迭代功能 | 包含迭代器所有功能,支持send/close |
| 使用场景 | 复杂自定义迭代逻辑 | 简单惰性序列、大数据处理 |
一句话总结 :生成器是 Python 为了简化迭代器开发提供的语法糖,日常开发优先用生成器。
六、为什么要用迭代器/生成器?
核心场景:处理大数据/超大文件
如果用列表存储 1 亿个数字,会直接占满内存;但生成器/迭代器只占用固定内存。
示例:读取超大文件(避免一次性加载到内存)
python
# 逐行读取文件,内存占用极低
def read_large_file(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
for line in f: # 文件对象本身就是迭代器
yield line
七、常见误区
- 迭代器只能遍历一次
python
gen = (x for x in [1,2,3])
print(list(gen)) # [1,2,3]
print(list(gen)) # [] (已耗尽)
- 生成器函数调用才生成对象
python
# 错误:函数没调用,不是生成器
print(my_generator(3)) # <generator object ...>
# 正确:必须赋值给变量使用
gen = my_generator(3)
yield不是终止函数 ,return才会终止生成器。
总结
- 可迭代对象 :能被
for遍历,如列表、字符串; - 迭代器:实现迭代协议,惰性计算、一次性遍历;
- 生成器 :
yield关键字实现的简化迭代器,开发效率更高; - 核心价值:节省内存、支持无限序列、处理大数据。