乐闻世界logo
搜索文章和话题

Python 中的迭代器和生成器有什么区别?

2月17日 21:45

Python 迭代器与生成器详解

迭代器(Iterator)

什么是迭代器

迭代器是一个实现了迭代协议的对象,它包含两个方法:

  • __iter__():返回迭代器对象本身
  • __next__():返回容器的下一个元素,如果没有更多元素则抛出 StopIteration 异常

迭代器示例

python
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): 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)

迭代器的特性

  1. 惰性求值:只在需要时才计算下一个值
  2. 节省内存:不需要一次性存储所有数据
  3. 单向遍历:只能向前遍历,不能回退
  4. 一次性使用:迭代器遍历后就不能再次使用
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 语句来创建。生成器函数在执行过程中会暂停并保存当前状态,下次调用时从暂停处继续执行。

生成器函数示例

python
def 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)) # 几乎不占用内存

生成器的优势

  1. 内存效率:不需要一次性生成所有值
  2. 惰性计算:只在需要时才计算值
  3. 无限序列:可以表示无限长的序列
  4. 管道处理:可以串联多个生成器
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. 处理大文件

python
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'): process_line(line) # 处理每一行

2. 数据管道

python
def 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. 斐波那契数列

python
def 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. 批量处理数据

python
def 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

python
def sub_generator(): yield 1 yield 2 def main_generator(): yield from sub_generator() # 委托给子生成器 yield 3 print(list(main_generator())) # [1, 2, 3]

2. 生成器发送值

python
def 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. 生成器抛出异常

python
def 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() # 生成器关闭

性能对比

python
import 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 模块

python
import 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 语句创建
  • 自动管理状态
  • 代码更简洁
  • 内存效率更高
  • 适合数据流处理

使用建议

  1. 小数据集:使用列表或元组
  2. 大数据集:使用生成器
  3. 复杂逻辑:使用迭代器类
  4. 数据管道:使用生成器表达式
  5. 无限序列:使用生成器函数

理解迭代器和生成器的区别,能够帮助编写更高效、更优雅的 Python 代码。

标签:Python