Python中的生成器和迭代器是处理序列数据的核心特性,二者都支持按需逐个获取元素,不需要提前将所有数据加载到内存中,非常适合处理大规模数据或者无限序列的场景。这种特性能够有效降低程序的内存占用,避免因为数据量过大导致内存溢出。

什么是迭代器
迭代器是实现了迭代器协议的对象,迭代器协议要求对象必须包含__iter__()方法和__next__()方法。__iter__()方法返回迭代器对象本身,__next__()方法返回序列中的下一个元素,当没有更多元素时,会抛出StopIteration异常。
Python中很多内置对象都是可迭代对象,比如列表、元组、字符串,但是它们本身不是迭代器,需要通过iter()函数转换为迭代器之后才能使用__next__()方法逐个获取元素。
自定义迭代器实现
下面是一个自定义迭代器的示例,实现返回指定范围内的偶数:
class EvenIterator:
def __init__(self, max_num):
self.max_num = max_num
self.current = 0
def __iter__(self):
# 返回迭代器本身
return self
def __next__(self):
# 找到下一个偶数
while self.current <= self.max_num:
if self.current % 2 == 0:
result = self.current
self.current += 1
return result
self.current += 1
# 没有更多元素时抛出异常
raise StopIteration
# 使用自定义迭代器
even_iter = EvenIterator(10)
print(next(even_iter)) # 输出0
print(next(even_iter)) # 输出2
print(next(even_iter)) # 输出4
什么是生成器
生成器是迭代器的一种简化实现方式,不需要手动实现__iter__()和__next__()方法,也不需要管理迭代状态,语法上更加简洁。生成器有两种实现方式:生成器函数和生成器表达式。
生成器函数
生成器函数是包含yield关键字的函数,调用生成器函数时不会立即执行函数体,而是返回一个生成器对象。每次调用生成器对象的__next__()方法时,函数会执行到yield语句处,返回yield后面的值,然后暂停执行,下次调用时从暂停的位置继续运行。
下面的示例用生成器函数实现同样返回指定范围内偶数的功能:
def even_generator(max_num):
current = 0
while current <= max_num:
if current % 2 == 0:
yield current
current += 1
# 使用生成器
even_gen = even_generator(10)
print(next(even_gen)) # 输出0
print(next(even_gen)) # 输出2
print(next(even_gen)) # 输出4
生成器表达式
生成器表达式是类似列表推导式的语法,但是使用圆括号包裹,返回的是一个生成器对象,而不是列表。生成器表达式的语法更加简洁,适合简单的生成逻辑。
示例:
# 生成0到10的偶数生成器 even_gen_expr = (i for i in range(11) if i % 2 == 0) print(next(even_gen_expr)) # 输出0 print(next(even_gen_expr)) # 输出2 print(next(even_gen_expr)) # 输出4
生成器与迭代器的核心区别
生成器属于迭代器的子类,所有生成器都是迭代器,但不是所有迭代器都是生成器。二者的主要区别如下:
| 对比项 | 迭代器 | 生成器 |
|---|---|---|
| 实现方式 | 需要手动实现__iter__()和__next__()方法 | 通过yield关键字或者生成器表达式实现,无需手动实现迭代器协议 |
| 代码复杂度 | 相对复杂,需要管理迭代状态 | 语法简洁,自动管理迭代状态 |
| 内存占用 | 按需返回元素,内存占用低 | 按需返回元素,内存占用低 |
| 适用场景 | 复杂的迭代逻辑,需要自定义迭代规则 | 简单的迭代逻辑,快速实现按需返回元素的需求 |
如何节省内存的遍历方式
生成器和迭代器之所以能节省内存,是因为它们不会一次性把所有元素加载到内存中,而是在每次需要元素的时候才生成对应的元素。比如要处理一个包含1000万个整数的序列,如果用列表存储,需要占用大量内存,而用生成器的话,只需要存储生成器的当前状态,每次获取元素时才计算下一个值。
下面是一个对比示例,分别用列表和生成器遍历大范围的数,查看内存占用情况:
import sys
# 列表方式存储1到10000000的数
num_list = [i for i in range(10000000)]
# 生成器方式存储1到10000000的数
num_generator = (i for i in range(10000000))
print(f"列表占用内存大小:{sys.getsizeof(num_list)} 字节")
print(f"生成器占用内存大小:{sys.getsizeof(num_generator)} 字节")
运行结果可以看到,生成器的内存占用远远小于列表,这就是生成器节省内存的核心原因。
常见使用场景
- 处理大文件:逐行读取大文件内容,不需要把整个文件加载到内存
- 生成无限序列:比如生成斐波那契数列,不需要提前定义序列长度
- 数据管道处理:多个生成器串联,逐个处理数据,减少中间结果的存储
- 流式数据处理:处理实时产生的数据流,按需获取和处理数据
注意事项
生成器和迭代器都是单向的,只能向前遍历,不能回退或者重置。如果需要重新遍历,需要重新创建生成器或者迭代器对象。另外,生成器函数中的yield语句会将函数暂停,局部变量会保存在生成器对象中,下次调用时恢复这些局部变量的值。
迭代器和生成器是Python中优化内存使用的重要特性,掌握两者的使用方式能够有效提升处理大规模数据时的程序性能,避免不必要的内存浪费。
在实际开发中,如果迭代逻辑比较简单,优先使用生成器,代码更简洁;如果迭代逻辑复杂,需要自定义更多的控制逻辑,可以选择自定义迭代器实现。