# 生成器是 Python 中一种特殊的迭代器,使用 yield 关键字创建,具有惰性计算特性。 # 1.1.什么是生成器? # 生成器是一种特殊的函数,具有以下特征: # 惰性计算:按需生成值,不立即计算所有结果 # 内存高效:一次只处理一个值,不存储整个序列 # 可迭代:可以用在 for 循环中 # 状态保持:记住上次执行的位置 # 协程特性:支持双向通信,可以接收外部数据 # 创建生成器的方法 # 生成器有两种主要创建方式: # 生成器函数:使用 yield 关键字的函数 # 生成器表达式:类似列表推导式的语法,但使用圆括号 # 生成器函数 vs 普通函数 # 特性 普通函数 生成器函数 # 返回值 使用 return 使用 yield # 执行方式 一次性执行完毕 可以暂停和恢复 # 内存使用 一次性创建所有结果 按需生成,节省内存 # 状态保持 不保持状态 保持执行状态 # 优势: # 内存效率高,适合处理大数据集 # 支持复杂的控制流 # 可以实现协程模式 # 代码更简洁易读 # 方法1:生成器函数 # 生成器函数使用 yield 关键字创建,每次遇到 yield 会暂停执行并返回值。 # 语法特点: # 使用 def 关键字定义 # 函数体内必须包含 yield 语句 # 调用时返回生成器对象,不立即执行 # 通过 next() 或 for 循环驱动执行 def my_generator(): """简单的生成器函数示例""" print("第一步") yield "A" print("第二步") yield "B" print("第三步") yield "C" # 创建生成器对象 gen = my_generator() # 逐步获取值 print(next(gen)) # 输出: 第一步 \n A print(next(gen)) # 输出: 第二步 \n B print(next(gen)) # 输出: 第三步 \n C # print(next(gen)) # 抛出 StopIteration 异常 # 使用 for 循环遍历 for value in my_generator(): print(f"获取到: {value}") # 重要特性: # 生成器只能遍历一次,用完即耗尽 # 每次 yield 会保存函数状态 # 适合处理大量数据或复杂逻辑 # 支持协程模式(双向通信) # 方法2:生成器表达式 # 生成器表达式是创建生成器最简洁的方式,语法类似列表推导式,但使用圆括号。 # 基本语法: # (expression for item in iterable [if condition]) # 创建平方数生成器 gen = (x**2 for x in range(5)) print(type(gen)) # print(next(gen)) # 0 print(next(gen)) # 1 print(next(gen)) # 4 print(next(gen)) # 9 print(next(gen)) # 16 # print(next(gen)) # StopIteration # 使用 for 循环遍历 for value in (x * 3 for x in range(4)): print(value, end=" ") # 输出: 0 3 6 9 # 带条件的生成器表达式 even_squares = (x**2 for x in range(10) if x % 2 == 0) print(list(even_squares)) # [0, 4, 16, 36, 64] # 优势: # 语法简洁,一行代码创建生成器 # 内存效率高,惰性求值 # 适合简单的数据转换和过滤 # 可以与其他函数链式组合 # 适用场景: # 简单的数据转换 # 一次性遍历 # 内存敏感的场景 # 与内置函数配合使用 # 列表推导式 vs 生成器表达式 # numbers = [1, 2, 3, 4, 5] # # 列表推导式 - 立即计算所有结果 # squares_list = [x**2 for x in numbers] # print(squares_list) # [1, 4, 9, 16, 25] # print(type(squares_list)) # # # 生成器表达式 - 惰性计算 # squares_gen = (x**2 for x in numbers) # print(squares_gen) # at 0x...> # print(type(squares_gen)) # # # 遍历生成器 # for square in squares_gen: # print(square, end=" ") # 1 4 9 16 25 # 特性 列表推导式 生成器表达式 # 语法 [expr for item in iterable] (expr for item in iterable) # 内存使用 一次性创建所有元素 按需生成,节省内存 # 执行时机 立即执行 惰性求值 # 可重复使用 是 否(只能遍历一次) # 适用场景 需要多次访问结果 一次性遍历,大数据集 # 生成器方法 # 生成器提供了几个特殊方法,支持更高级的交互和控制。 # 4.1.send() 方法 - 向生成器发送值 # send() 方法可以向生成器发送值,实现双向通信。 # 使用规则: # 首次启动生成器必须使用 next() # 只有生成器暂停在 yield 后才能使用 send() # send() 会返回生成器产生的下一个值 def simple_echo(): """简单的回声生成器""" received = yield "请给我一个值" yield f"你发来的是:{received}" gen = simple_echo() print(next(gen)) # 输出: 请给我一个值 print(gen.send("Hello")) # 输出: 你发来的是:Hello # 执行流程: # next(gen) 启动生成器,执行到第一个 yield # gen.send("Hello") 将值发送给生成器,继续执行 # 生成器接收值并处理,产生下一个结果 # 应用场景: # 协程编程 # 状态机实现 # 数据管道处理 # 与外部系统交互 # throw() 方法 - 向生成器抛出异常 # throw() 方法可以在生成器暂停处抛出异常,实现异常处理机制。 # 使用场景: # 外部主动通知生成器发生错误 # 优雅地中断生成器执行 # 实现异常处理和恢复逻辑 def exception_generator(): """演示异常处理的生成器""" try: yield "开始" yield "继续" yield "结束" except ValueError as e: yield f"捕获异常: {e}" yield "恢复执行" # 使用示例 gen = exception_generator() print(next(gen)) # 开始 print(next(gen)) # 继续 print(gen.throw(ValueError("测试异常"))) # 捕获异常: 测试异常 print(next(gen)) # 恢复执行 # 异常处理流程: # 生成器正常执行到 yield 语句 # 外部调用 throw() 抛出异常 # 生成器内部捕获异常并处理 # 可以选择恢复执行或终止生成器 # 注意事项: # 如果异常未被捕获,会向外传播 # 异常处理完成后,生成器可以继续执行 # 适合实现错误恢复和资源清理 # close() 方法 - 关闭生成器 # close() 方法用于主动终止生成器,触发资源清理。 # 使用场景: # 读取大文件时提前终止 # 数据库连接管理 # 网络连接清理 # 资源释放 def closable_generator(): """可关闭的生成器示例""" try: yield "第一步" yield "第二步" yield "第三步" except GeneratorExit: print("生成器被关闭,正在清理资源") raise # 必须重新抛出异常 # 使用示例 gen = closable_generator() print(next(gen)) # 第一步 gen.close() # 生成器被关闭,正在清理资源 # 后续调用 next(gen) 会抛出 StopIteration # 关闭流程: # 调用 close() 方法 # 生成器在 yield 处抛出 GeneratorExit 异常 # 生成器内部捕获异常并清理资源 # 必须重新抛出异常,否则会触发 RuntimeError # 注意事项: # 只能在生成器暂停时关闭 # 关闭后再次调用 next() 会抛出 StopIteration # 适合实现资源管理和清理逻辑 # 高级生成器模式 # 6.1.生成器委托 (yield from) # yield from 语句可以简化生成器委托,让代码更简洁。 # 基本语法: # yield from iterable # 原始写法 def chain_generators_old(*iterables): for iterable in iterables: for item in iterable: yield item # 使用 yield from def chain_generators(*iterables): for iterable in iterables: yield from iterable # 使用示例 gen1 = (x for x in range(3)) gen2 = (x for x in range(3, 6)) gen3 = (x for x in range(6, 9)) chained = chain_generators(gen1, gen2, gen3) print(list(chained)) # [0, 1, 2, 3, 4, 5, 6, 7, 8] # 优势: # 代码更简洁,无需嵌套循环 # 自动处理子生成器的返回值 # 支持协程通信 # 适合递归遍历树形结构 # 应用场景: # 拼接多个生成器 # 递归遍历树形结构 # 协程通信 # 管道式编程 # 协程模式 # 协程是生成器的重要扩展,支持双向通信和协作式编程。 # 协程特点: # 使用 yield 暂停和恢复执行 # 支持双向数据交换 # 可以接收外部发送的数据 # 适合异步任务调度 def average_coroutine(): """协程模式:实时计算平均值""" total = 0 count = 0 average = 0 while True: value = yield average # 接收外部数据 if value is None: break total += value count += 1 average = total / count return average # 使用协程 coro = average_coroutine() next(coro) # 预激协程 print(coro.send(10)) # 10.0 print(coro.send(20)) # 15.0 print(coro.send(30)) # 20.0 try: coro.send(None) # 终止协程 except StopIteration as e: print("最终平均值:", e.value) # 最终平均值: 20.0 # 协程优势: # 支持双向通信 # 可以暂停和恢复执行 # 适合异步编程 # 代码结构清晰 # 应用场景: # 异步任务调度 # 流式数据处理 # 事件驱动编程 # 状态机实现 # 生成器与迭代器 # 7.1.关系说明 # 生成器是 Python 中实现迭代器协议的一种简便方式: # 生成器本身就是特殊的迭代器,实现了 __iter__() 和 __next__() 方法 # 所有生成器都能用于 for 循环,与其他迭代器无缝兼容 # 生成器提供了更简洁的迭代器实现方式