迭代器与生成器详解
·
Python 中的迭代器与生成器详解:原理、实现与实战
在 Python 中,迭代器(Iterator)和生成器(Generator)是实现惰性计算、节省内存、优化性能的核心工具。
一、什么是迭代器(Iterator)?
1.1 基本定义
迭代器是一种对象,它实现了 Python 的迭代协议,包含两个方法:
__iter__():返回迭代器本身。__next__():返回下一个值,如果没有更多元素,抛出StopIteration异常。
1.2 迭代器的特点
- 惰性计算:不会一次性加载所有数据,而是按需生成,适用于数据量大的场景。
- 单向性:只能向前迭代,不能回退。
- 节省内存:无需保存整个序列在内存中。
1.3 创建迭代器对象
我们可以通过内置函数 iter() 将可迭代对象转换为迭代器:
L = [1, 3, 5, 7]
it = iter(L) # 获取迭代器对象
print(next(it)) # 输出 1
print(next(it)) # 输出 3
解释: iter(L) 会返回一个迭代器,该对象能逐个读取列表的元素。每次调用 next() 都会返回下一个值,直到抛出 StopIteration。
二、自定义迭代器类
当你需要完全控制迭代逻辑时,可以通过定义一个类手动实现 __iter__() 和 __next__():
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self # 返回自身作为迭代器
def __next__(self):
if self.index < len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIteration
# 使用自定义迭代器
my_iter = MyIterator([10, 20, 30])
for item in my_iter:
print(item)
解释: MyIterator 能逐个输出列表中的元素,每次迭代内部索引递增。当超出范围时,会自动终止迭代。
三、什么是生成器(Generator)?
3.1 基本定义
生成器是使用 yield 的函数或表达式,具有迭代器所有功能,同时具有更简洁的语法。
3.2 生成器函数的特点
- 使用
yield替代return。 - 每次调用
next(),从上一次yield处恢复执行。 - 自动实现
__iter__()和__next__(),无需显式定义。
3.3 生成器函数示例
def my_generator():
print("Start")
yield 1
print("Continue")
yield 2
print("End")
yield 3
gen = my_generator()
for val in gen:
print(val)
输出解释:
Start
1
Continue
2
End
3
每次遇到 yield,生成器暂停并返回值;下一次迭代会从 yield 的下一行继续执行。
四、生成器表达式
Python 提供类似列表推导式的语法,用小括号表示生成器表达式:
gen_expr = (x**2 for x in range(5))
print(next(gen_expr)) # 0
print(next(gen_expr)) # 1
解释:这种方式生成一个迭代器对象,按需计算每个值,节省内存,适用于大数据处理。
五、经典实战示例
5.1 斐波那契数列生成器
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield b
a, b = b, a + b
for num in fibonacci(10):
print(num, end=' ')
输出:
1 1 2 3 5 8 13 21 34 55
解释: 使用 yield 保持状态,可以轻松生成大量数据。
5.2 倒数生成器
def Descendorder(n):
while n > 0:
yield n
n -= 1
gen = Descendorder(5)
print(next(gen)) # 5
print(next(gen)) # 4
for i in gen:
print('for循环:', i)
输出:
5
4
for循环: 3
for循环: 2
for循环: 1
5.3 流式读取大文件
def read_large_file(file_path):
with open(file_path, 'r') as file:
for line in file:
yield line.strip()
# 假设文件很大,逐行读取避免内存爆炸
for line in read_large_file('large_file.txt'):
print(line)
5.4 图像标签生成器:结合迭代器与 PIL 处理图像元数据
import os
from PIL import Image
print("当前工作目录:", os.getcwd())
class DataLoader:
def __init__(self, filelist):
self.filelist = filelist
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.filelist):
p = self.filelist[self.index]
self.index += 1
# 解析图像文件名中的坐标数据
axis = tuple([tuple(map(int, a.split('&'))) for a in p.split('-')[2].split('_')])
# 计算车牌区域的宽和高
width = axis[1][0] - axis[0][0]
height = axis[1][1] - axis[0][1]
# 计算中心点坐标
centerX = (axis[0][0] + axis[1][0]) / 2
centerY = (axis[0][1] + axis[1][1]) / 2
# 归一化到图像大小范围(0~1)
readImg = Image.open('./imagestest/' + p)
size = readImg.size
width /= size[0]
height /= size[1]
centerX /= size[0]
centerY /= size[1]
# 构造标签字符串
label = f'{centerX} {centerY} {width} {height}'
return label, p.split('.')[0]
else:
raise StopIteration
# 获取图像文件列表并处理
filelist = os.listdir('./imagestest')
datasets = DataLoader(filelist)
for label, filename in datasets:
print(label)
with open(f'./labels/{filename}.txt', mode='w', encoding='utf-8') as f:
f.write(label)
print(f'{filename} 处理done...')
解释:该例展示了如何将图像处理与迭代器结合,通过文件名提取标签并写入目标路径,是图像标注和机器学习训练准备的常见任务。
六、应用场景总结
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 自定义复杂遍历逻辑 | 迭代器 | 精细控制状态 |
| 简单或数据流生成 | 生成器 | 写法简单,自动管理状态 |
| 处理大文件/数据管道 | 生成器 | 惰性加载,节省内存 |
| 无限序列,如斐波那契、素数 | 生成器 | 延迟计算,避免爆内存 |
七、迭代器 vs 生成器:区别对比表
| 特性 | 迭代器 | 生成器 |
|---|---|---|
| 实现方式 | 通过类实现 __iter__ + __next__ |
通过函数 + yield 实现 |
| 代码复杂度 | 相对复杂 | 非常简洁 |
| 状态管理 | 手动管理 | 自动保存中断状态 |
| 性能 | 高效 | 更高效(尤其是大数据) |
| 内存占用 | 低 | 更低 |
八、小结
- 迭代器更适合需要完全控制迭代过程的场景,灵活但代码冗长。
- 生成器适合快速实现可迭代对象,尤其在数据量大时节省资源。
- 生成器表达式提供更加紧凑的写法,是列表推导式的高效替代。
更多推荐
所有评论(0)