342 lines
9.3 KiB
Python
342 lines
9.3 KiB
Python
# 生成器是 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)) # <class 'generator'>
|
|
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)) # <class 'list'>
|
|
|
|
# # 生成器表达式 - 惰性计算
|
|
# squares_gen = (x**2 for x in numbers)
|
|
# print(squares_gen) # <generator object <genexpr> at 0x...>
|
|
# print(type(squares_gen)) # <class 'generator'>
|
|
|
|
# # 遍历生成器
|
|
# 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 循环,与其他迭代器无缝兼容
|
|
# 生成器提供了更简洁的迭代器实现方式
|