Python 迭代器与生成器详解
迭代器(Iterator)
什么是迭代器
迭代器是一个实现了迭代协议的对象,它包含两个方法:
__iter__():返回迭代器对象本身__next__():返回容器的下一个元素,如果没有更多元素则抛出StopIteration异常
迭代器示例
pythonclass 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): raise StopIteration value = self.data[self.index] self.index += 1 return value # 使用迭代器 my_iter = MyIterator([1, 2, 3, 4, 5]) for item in my_iter: print(item)
迭代器的特性
- 惰性求值:只在需要时才计算下一个值
- 节省内存:不需要一次性存储所有数据
- 单向遍历:只能向前遍历,不能回退
- 一次性使用:迭代器遍历后就不能再次使用
python# 迭代器的一次性特性 my_list = [1, 2, 3] my_iter = iter(my_list) print(list(my_iter)) # [1, 2, 3] print(list(my_iter)) # [] - 迭代器已耗尽
可迭代对象(Iterable)
什么是可迭代对象
可迭代对象是实现了 __iter__() 方法的对象,该方法返回一个迭代器。常见的可迭代对象包括列表、元组、字符串、字典、集合等。
可迭代对象示例
python# 内置可迭代对象 my_list = [1, 2, 3] my_tuple = (1, 2, 3) my_string = "hello" my_dict = {'a': 1, 'b': 2} my_set = {1, 2, 3} # 检查是否可迭代 from collections.abc import Iterable print(isinstance(my_list, Iterable)) # True print(isinstance(123, Iterable)) # False
可迭代对象与迭代器的关系
python# 可迭代对象通过 iter() 函数获取迭代器 my_list = [1, 2, 3] my_iterator = iter(my_list) print(next(my_iterator)) # 1 print(next(my_iterator)) # 2 print(next(my_iterator)) # 3 # print(next(my_iterator)) # StopIteration
生成器(Generator)
什么是生成器
生成器是一种特殊的迭代器,使用函数和 yield 语句来创建。生成器函数在执行过程中会暂停并保存当前状态,下次调用时从暂停处继续执行。
生成器函数示例
pythondef simple_generator(): yield 1 yield 2 yield 3 # 使用生成器 gen = simple_generator() print(next(gen)) # 1 print(next(gen)) # 2 print(next(gen)) # 3
生成器表达式
python# 生成器表达式(类似列表推导式) gen_expr = (x * x for x in range(10)) print(list(gen_expr)) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # 生成器表达式节省内存 # 列表推导式 list_comp = [x * x for x in range(1000000)] # 占用大量内存 # 生成器表达式 gen_expr = (x * x for x in range(1000000)) # 几乎不占用内存
生成器的优势
- 内存效率:不需要一次性生成所有值
- 惰性计算:只在需要时才计算值
- 无限序列:可以表示无限长的序列
- 管道处理:可以串联多个生成器
python# 无限序列生成器 def infinite_sequence(): num = 0 while True: yield num num += 1 # 使用无限序列 gen = infinite_sequence() for i in range(10): print(next(gen)) # 0, 1, 2, ..., 9
迭代器与生成器的对比
相同点
- 都实现了迭代协议
- 都支持惰性求值
- 都可以使用
next()函数获取下一个值 - 都可以在
for循环中使用
不同点
| 特性 | 迭代器 | 生成器 |
|---|---|---|
| 实现方式 | 实现 __iter__ 和 __next__ 方法 | 使用 yield 语句 |
| 代码复杂度 | 需要手动管理状态 | 自动管理状态 |
| 内存占用 | 需要存储所有数据 | 只保存当前状态 |
| 代码简洁性 | 相对复杂 | 更简洁 |
代码对比
python# 迭代器实现 class SquaresIterator: def __init__(self, n): self.n = n self.current = 0 def __iter__(self): return self def __next__(self): if self.current >= self.n: raise StopIteration result = self.current ** 2 self.current += 1 return result # 生成器实现 def squares_generator(n): for i in range(n): yield i ** 2 # 使用对比 print("迭代器:", list(SquaresIterator(5))) # [0, 1, 4, 9, 16] print("生成器:", list(squares_generator(5))) # [0, 1, 4, 9, 16]
实际应用场景
1. 处理大文件
pythondef 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'): process_line(line) # 处理每一行
2. 数据管道
pythondef read_data(source): """读取数据""" for item in source: yield item def filter_data(data, predicate): """过滤数据""" for item in data: if predicate(item): yield item def transform_data(data, func): """转换数据""" for item in data: yield func(item) # 使用数据管道 data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] pipeline = transform_data( filter_data( read_data(data), lambda x: x % 2 == 0 # 过滤偶数 ), lambda x: x * 2 # 转换为两倍 ) print(list(pipeline)) # [4, 8, 12, 16, 20]
3. 斐波那契数列
pythondef fibonacci(): """生成斐波那契数列""" a, b = 0, 1 while True: yield a a, b = b, a + b # 获取前 10 个斐波那契数 fib = fibonacci() fib_sequence = [next(fib) for _ in range(10)] print(fib_sequence) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
4. 批量处理数据
pythondef batch_generator(data, batch_size): """将数据分批处理""" for i in range(0, len(data), batch_size): yield data[i:i + batch_size] # 使用批量生成器 data = list(range(100)) for batch in batch_generator(data, 10): print(f"处理批次: {batch}")
生成器的高级用法
1. yield from
pythondef sub_generator(): yield 1 yield 2 def main_generator(): yield from sub_generator() # 委托给子生成器 yield 3 print(list(main_generator())) # [1, 2, 3]
2. 生成器发送值
pythondef accumulator(): total = 0 while True: value = yield total if value is not None: total += value acc = accumulator() next(acc) # 启动生成器 print(acc.send(10)) # 10 print(acc.send(20)) # 30 print(acc.send(30)) # 60
3. 生成器抛出异常
pythondef my_generator(): try: while True: value = yield print(f"收到值: {value}") except ValueError: print("捕获到 ValueError") finally: print("生成器关闭") gen = my_generator() next(gen) gen.send(1) # 收到值: 1 gen.throw(ValueError) # 捕获到 ValueError gen.close() # 生成器关闭
性能对比
pythonimport time import sys # 内存使用对比 def list_comprehension(n): return [i ** 2 for i in range(n)] def generator_expression(n): return (i ** 2 for i in range(n)) # 内存占用 list_obj = list_comprehension(1000000) gen_obj = generator_expression(1000000) print(f"列表占用内存: {sys.getsizeof(list_obj)} 字节") print(f"生成器占用内存: {sys.getsizeof(gen_obj)} 字节") # 执行时间对比 start = time.time() sum([i ** 2 for i in range(1000000)]) print(f"列表推导式耗时: {time.time() - start:.4f} 秒") start = time.time() sum(i ** 2 for i in range(1000000)) print(f"生成器表达式耗时: {time.time() - start:.4f} 秒")
最佳实践
1. 选择合适的工具
python# 需要多次访问数据 - 使用列表 data = [1, 2, 3, 4, 5] result1 = sum(data) result2 = max(data) # 只需要一次遍历 - 使用生成器 data = (i for i in range(1000000)) result = sum(data)
2. 避免过早求值
python# 不好的做法 def get_all_data(): return [process_item(item) for item in large_dataset] # 好的做法 def get_data_generator(): for item in large_dataset: yield process_item(item)
3. 使用 itertools 模块
pythonimport itertools # 无限计数器 counter = itertools.count(start=0, step=2) print(list(itertools.islice(counter, 5))) # [0, 2, 4, 6, 8] # 循环迭代器 cycle = itertools.cycle([1, 2, 3]) print(list(itertools.islice(cycle, 7))) # [1, 2, 3, 1, 2, 3, 1] # 链接迭代器 chain = itertools.chain([1, 2], [3, 4], [5, 6]) print(list(chain)) # [1, 2, 3, 4, 5, 6]
总结
迭代器
- 实现了
__iter__和__next__方法 - 手动管理状态
- 适合复杂的迭代逻辑
- 可以重复创建
生成器
- 使用
yield语句创建 - 自动管理状态
- 代码更简洁
- 内存效率更高
- 适合数据流处理
使用建议
- 小数据集:使用列表或元组
- 大数据集:使用生成器
- 复杂逻辑:使用迭代器类
- 数据管道:使用生成器表达式
- 无限序列:使用生成器函数
理解迭代器和生成器的区别,能够帮助编写更高效、更优雅的 Python 代码。