迭代器与生成器
在 Python 中,迭代器(Iterator)和生成器(Generator)是处理数据流的核心工具。它们允许我们以惰性(Lazy)的方式处理数据,即一次只处理一个数据项,而不是将整个数据集加载到内存中。这通过节省内存提高了处理大数据集的效率。
1. 迭代器 (Iterator)
迭代器是一个可以记住遍历位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
可迭代对象 (Iterable) vs 迭代器 (Iterator)
- 可迭代对象 (Iterable): 可以直接作用于
for循环的对象统称为可迭代对象,如list,tuple,dict,set,str等。 - 迭代器 (Iterator): 可以被
next()函数调用并不断返回下一个值的对象称为迭代器。
创建和使用迭代器
使用 iter() 函数可以将可迭代对象转换成迭代器。
# 列表是可迭代对象
my_list = [1, 2, 3]
# 创建迭代器
my_iter = iter(my_list)
# 使用 next() 获取元素
print(next(my_iter)) # 输出: 1
print(next(my_iter)) # 输出: 2
print(next(my_iter)) # 输出: 3
#再次调用会抛出 StopIteration 异常
# print(next(my_iter))
关于内存节省的说明
你可能注意到了,上面的例子并没有节省内存,因为列表 [1, 2, 3] 已经完整地存在于内存中了。
迭代器真正节省内存的威力在于惰性求值(Lazy Evaluation):通常我们可以直接通过算法生成数据,或者从文件流中逐行读取,而不需要先创建一个完整的列表。下面的“自定义迭代器”和“生成器”章节将展示这种“用一个生成一个”的高效用法。
自定义迭代器
要创建一个自定义迭代器,需要在类中实现 __iter__() 和 __next__() 方法。
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 5:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
2. 生成器 (Generator)
生成器是一种特殊的迭代器,它比自定义迭代器更加简洁优雅。生成器函数使用 yield 语句返回数据。
生成器函数
当一个函数包含 yield 关键字时,它就不再是一个普通的函数,而是一个生成器函数。调用生成器函数会返回一个生成器对象。
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
执行流程
- 调用生成器函数创建生成器对象。
- 调用
next()时,函数开始执行,直到遇到yield。 yield返回一个值,并暂停函数的执行,保存当前状态(变量值、指令指针等)。- 再次调用
next()时,函数从上次暂停的地方恢复执行。
示例:斐波那契数列
使用生成器生成斐波那契数列,可以无限生成而不会占用大量内存。
def fibonacci(n):
a, b = 0, 1
counter = 0
while counter < n:
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10)
for x in f:
print(x, end=" ")
# 输出: 0 1 1 2 3 5 8 13 21 34
3. 迭代器 vs 生成器 vs 列表
| 特性 | 列表 (List) | 迭代器 (Iterator) | 生成器 (Generator) |
|---|---|---|---|
| 内存占用 | 高 (存储所有元素) | 低 (仅保存状态) | 低 (仅保存状态) |
| 访问方式 | 索引访问,切片 | next() 逐个访问 |
next() 逐个访问 |
| 实现难度 | 最简单 | 需实现类方法 | 简单 (yield 函数) |
| 使用场景 | 数据量小,需多次访问 | 自定义遍历逻辑 | 数据流处理,大数据量 |
总结
- 凡是可用于
for循环的对象都是Iterable。 - 凡是可用于
next()函数的对象都是Iterator。 - 生成器是一种实现迭代器的便捷方式,推荐优先使用。
- 在处理大量数据或无限序列时,使用迭代器和生成器可以显著节省内存。
下一步
生成器虽然强大,但也需要妥善管理。在 Python 中,有一种更通用的“资源管理”神器,常用于文件操作和数据库连接。