目录
专栏导读
🌸 欢迎来到Python办公自动化专栏---Python处理办公问题,解放您的双手
🏳️🌈 个人博客主页:请点击------> 个人的博客主页 求收藏
🏳️🌈 Github主页:请点击------> Github主页 求Star⭐
🏳️🌈 知乎主页:请点击------> 知乎主页 求关注
🏳️🌈 CSDN博客主页:请点击------> CSDN的博客主页 求关注
👍 该系列文章专栏:请点击------>Python办公自动化专栏 求订阅
🕷 此外还有爬虫专栏:请点击------>Python爬虫基础专栏 求订阅
📕 此外还有python基础专栏:请点击------>Python基础学习专栏 求订阅
文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
❤️ 欢迎各位佬关注! ❤️
前言
引言
生成器(Generator)是Python中一个强大而优雅的特性,它让我们能够创建惰性求值的迭代器。与一次性生成所有数据的列表不同,生成器按需生成数据,大大节省了内存。本文将深入探讨生成器的工作原理、使用场景和高级技巧。
生成器基础
什么是生成器
生成器是一种特殊的迭代器,它使用yield关键字来产生值。与普通函数不同,生成器函数在每次调用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
# print(next(gen)) # StopIteration
生成器 vs 列表
python
import sys
import time
def list_version():
"""返回包含100万个数字的列表"""
return [i * 2 for i in range(1000000)]
def generator_version():
"""生成100万个数字的生成器"""
for i in range(1000000):
yield i * 2
# 内存对比
list_data = list_version()
print(f"列表内存占用: {sys.getsizeof(list_data)} 字节")
gen_data = generator_version()
print(f"生成器内存占用: {sys.getsizeof(gen_data)} 字节")
# 性能对比
def time_list():
start = time.time()
result = [i * 2 for i in range(1000000)]
return time.time() - start
def time_generator():
start = time.time()
result = (i * 2 for i in range(1000000)) # 生成器表达式
# 强制求值以对比性能
list(result)
return time.time() - start
print(f"列表创建时间: {time_list():.4f}秒")
print(f"生成器创建时间: {time_generator():.4f}秒")
yield详解
yield的工作原理
python
def fibonacci_generator(n):
"""生成斐波那契数列"""
a, b = 0, 1
count = 0
while count < n:
print(f"生成第{count + 1}个值: {a}")
yield a
a, b = b, a + b
count += 1
print(f"继续执行,当前状态: a={a}, b={b}")
# 逐步执行观察yield的行为
fib_gen = fibonacci_generator(5)
print("第一次调用:")
value1 = next(fib_gen)
print(f"获得值: {value1}")
print("\n第二次调用:")
value2 = next(fib_gen)
print(f"获得值: {value2}")
yield与return的区别
python
def with_return():
"""普通函数使用return"""
print("开始执行")
return 1
print("这行代码不会执行")
return 2
def with_yield():
"""生成器函数使用yield"""
print("开始执行")
yield 1
print("继续执行")
yield 2
print("执行结束")
# 对比执行
print("=== return版本 ===")
result = with_return()
print(f"结果: {result}")
print("\n=== yield版本 ===")
gen = with_yield()
print("第一次调用:")
val1 = next(gen)
print(f"获得值: {val1}")
print("\n第二次调用:")
val2 = next(gen)
print(f"获得值: {val2}")
print("\n第三次调用:")
try:
val3 = next(gen)
except StopIteration:
print("生成器结束")
生成器表达式
基本语法
生成器表达式是创建生成器的简洁方式,语法类似于列表推导式,但使用圆括号:
python
# 列表推导式(立即求值)
list_comp = [x * 2 for x in range(5)]
print(f"列表推导式结果: {list_comp}")
# 生成器表达式(惰性求值)
gen_expr = (x * 2 for x in range(5))
print(f"生成器表达式结果: {gen_expr}")
print(f"转换为列表: {list(gen_expr)}")
性能对比
python
import memory_profiler
import time
def memory_test_list():
"""测试列表推导式的内存使用"""
# 创建包含1000万个数字的列表
data = [i ** 2 for i in range(10000000)]
return sum(data)
def memory_test_generator():
"""测试生成器表达式的内存使用"""
# 创建生成1000万个数字的生成器
data = (i ** 2 for i in range(10000000))
return sum(data)
# 内存使用对比(简化版本)
print("=== 内存使用对比 ===")
# 列表版本
start_mem = memory_profiler.memory_usage()[0]
start_time = time.time()
result_list = memory_test_list()
list_time = time.time() - start_time
list_mem = memory_profiler.memory_usage()[0] - start_mem
print(f"列表版本 - 结果: {result_list}")
print(f"列表版本 - 时间: {list_time:.2f}秒")
# 生成器版本
start_mem = memory_profiler.memory_usage()[0]
start_time = time.time()
result_gen = memory_test_generator()
gen_time = time.time() - start_time
gen_mem = memory_profiler.memory_usage()[0] - start_mem
print(f"生成器版本 - 结果: {result_gen}")
print(f"生成器版本 - 时间: {gen_time:.2f}秒")
高级生成器技巧
生成器委托(yield from)
python
def subgenerator():
"""子生成器"""
yield 1
yield 2
return "子生成器完成"
def main_generator():
"""主生成器,委托给子生成器"""
yield 0
result = yield from subgenerator() # 委托给子生成器
print(f"子生成器返回值: {result}")
yield 3
# 使用生成器委托
gen = main_generator()
for value in gen:
print(f"主生成器产出: {value}")
双向通信
生成器不仅可以产出值,还可以接收值:
python
def echo_generator():
"""回显生成器,接收并返回发送的值"""
value = yield "准备就绪" # 第一次yield产出初始值
while True:
received = yield f"收到: {value}"
value = received
# 双向通信示例
echo = echo_generator()
print(next(echo)) # 启动生成器
print(echo.send("Hello"))
print(echo.send("World"))
print(echo.send("Python"))
throw和close方法
python
def robust_generator():
"""演示异常处理的生成器"""
try:
yield 1
yield 2
yield 3
except ValueError as e:
print(f"捕获到ValueError: {e}")
yield f"错误处理: {e}"
finally:
print("生成器清理工作")
yield "清理完成"
# 测试异常处理
gen = robust_generator()
print(next(gen)) # 产出1
print(next(gen)) # 产出2
# 向生成器抛出异常
try:
result = gen.throw(ValueError, "测试异常")
print(f"异常处理结果: {result}")
except StopIteration:
print("生成器结束")
# 关闭生成器
gen2 = robust_generator()
next(gen2)
gen2.close() # 触发GeneratorExit
实际应用案例
1. 大文件处理
python
def read_large_file(file_path, chunk_size=1024):
"""分块读取大文件"""
with open(file_path, 'rb') as file:
while True:
chunk = file.read(chunk_size)
if not chunk:
break
yield chunk
def process_large_file(file_path):
"""处理大文件,统计行数"""
def read_lines():
with open(file_path, 'r', encoding='utf-8') as file:
for line in file:
yield line.strip()
line_count = 0
word_count = 0
for line in read_lines():
line_count += 1
word_count += len(line.split())
# 每1000行输出一次进度
if line_count % 1000 == 0:
print(f"已处理{line_count}行,{word_count}个单词")
return line_count, word_count
# 创建测试文件
test_content = ""
for i in range(10000):
test_content += f"这是第{i}行测试数据,包含一些单词\n"
with open('test_large_file.txt', 'w', encoding='utf-8') as f:
f.write(test_content)
# 测试大文件处理
lines, words = process_large_file('test_large_file.txt')
print(f"总行数: {lines}, 总单词数: {words}")
2. 无限序列生成
python
def infinite_fibonacci():
"""生成无限的斐波那契数列"""
a, b = 0, 1
while True:
yield a
a, b = b, a + b
def prime_generator():
"""生成素数序列"""
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
n = 2
while True:
if is_prime(n):
yield n
n += 1
# 使用无限序列
fib_gen = infinite_fibonacci()
fib_10 = [next(fib_gen) for _ in range(10)]
print(f"前10个斐波那契数: {fib_10}")
prime_gen = prime_generator()
prime_10 = [next(prime_gen) for _ in range(10)]
print(f"前10个素数: {prime_10}")
3. 数据管道处理
python
def data_cleaner(data_source):
"""数据清洗生成器"""
for item in data_source:
# 去除空白字符
cleaned = item.strip()
# 过滤空字符串
if cleaned:
yield cleaned
def data_transformer(cleaned_data):
"""数据转换生成器"""
for item in cleaned_data:
# 转换为大写
yield item.upper()
def data_filter(transformed_data, min_length=3):
"""数据过滤生成器"""
for item in transformed_data:
# 过滤长度小于最小值的字符串
if len(item) >= min_length:
yield item
# 创建数据处理管道
def data_pipeline(raw_data):
"""完整的数据处理管道"""
cleaned = data_cleaner(raw_data)
transformed = data_transformer(cleaned)
filtered = data_filter(transformed)
return filtered
# 测试数据管道
raw_data = [" hello ", "", " world ", " py ", " python ", " ai "]
processed_data = list(data_pipeline(raw_data))
print(f"处理结果: {processed_data}")
4. 异步生成器模拟
python
import asyncio
import random
async def async_data_generator():
"""异步数据生成器模拟"""
for i in range(5):
# 模拟异步操作
await asyncio.sleep(random.uniform(0.1, 0.5))
yield f"异步数据{i}"
# 同步版本(用于对比)
def sync_data_generator():
"""同步数据生成器"""
for i in range(5):
# 模拟耗时操作
time.sleep(random.uniform(0.1, 0.5))
yield f"同步数据{i}"
# 测试同步生成器
print("=== 同步生成器测试 ===")
start_time = time.time()
for data in sync_data_generator():
print(f"收到数据: {data}")
sync_time = time.time() - start_time
print(f"同步总耗时: {sync_time:.2f}秒")
生成器模式与设计
生成器工厂模式
python
def generator_factory(start, step=1):
"""生成器工厂,创建不同类型的序列生成器"""
def arithmetic_sequence():
"""等差数列生成器"""
current = start
while True:
yield current
current += step
def geometric_sequence():
"""等比数列生成器"""
current = start
while True:
yield current
current *= step
def fibonacci_like():
"""类斐波那契数列生成器"""
a, b = start, step
while True:
yield a
a, b = b, a + b
return {
'arithmetic': arithmetic_sequence,
'geometric': geometric_sequence,
'fibonacci': fibonacci_like
}
# 使用生成器工厂
factory = generator_factory(1, 2)
# 创建不同类型的生成器
arith_gen = factory['arithmetic']()
geo_gen = factory['geometric']()
fib_gen = factory['fibonacci']()
print("等差数列前5项:", [next(arith_gen) for _ in range(5)])
print("等比数列前5项:", [next(geo_gen) for _ in range(5)])
print("类斐波那契前5项:", [next(fib_gen) for _ in range(5)])
状态机生成器
python
def state_machine_generator():
"""使用生成器实现状态机"""
state = 'START'
while True:
if state == 'START':
input_val = yield '开始状态'
if input_val == 'go':
state = 'PROCESSING'
else:
state = 'ERROR'
elif state == 'PROCESSING':
input_val = yield '处理中'
if input_val == 'complete':
state = 'COMPLETE'
elif input_val == 'error':
state = 'ERROR'
else:
yield '继续处理中'
elif state == 'COMPLETE':
yield '完成状态'
state = 'START' # 重置到开始状态
elif state == 'ERROR':
yield '错误状态'
state = 'START' # 重置到开始状态
# 测试状态机
sm = state_machine_generator()
print(next(sm)) # 开始状态
print(sm.send('go')) # 处理中
print(sm.send('processing')) # 继续处理中
print(sm.send('complete')) # 完成状态
print(next(sm)) # 开始状态(重置)
性能优化与最佳实践
内存使用优化
python
def memory_efficient_processing():
"""内存高效的数据处理示例"""
# 传统方法:一次性加载所有数据
def traditional_method(filename):
with open(filename, 'r') as f:
lines = f.readlines() # 所有数据加载到内存
results = []
for line in lines:
processed = line.strip().upper()
if len(processed) > 5:
results.append(processed)
return results
# 生成器方法:流式处理
def generator_method(filename):
with open(filename, 'r') as f:
for line in f: # 逐行处理
processed = line.strip().upper()
if len(processed) > 5:
yield processed
# 创建测试文件
test_file = 'memory_test.txt'
with open(test_file, 'w') as f:
for i in range(100000):
f.write(f"这是第{i}行测试数据\n")
# 测试内存使用
import tracemalloc
# 测试传统方法
tracemalloc.start()
traditional_result = traditional_method(test_file)
traditional_memory = tracemalloc.get_traced_memory()[1]
tracemalloc.stop()
# 测试生成器方法
tracemalloc.start()
generator_result = list(generator_method(test_file))
generator_memory = tracemalloc.get_traced_memory()[1]
tracemalloc.stop()
print(f"传统方法内存使用: {traditional_memory / 1024 / 1024:.2f} MB")
print(f"生成器方法内存使用: {generator_memory / 1024 / 1024:.2f} MB")
print(f"结果数量: {len(traditional_result)} = {len(generator_result)}")
# 运行内存测试
memory_efficient_processing()
生成器链式调用
python
def chained_generators():
"""生成器链式调用示例"""
def source_generator():
"""数据源生成器"""
for i in range(20):
yield i
def filter_even(source):
"""过滤偶数"""
for item in source:
if item % 2 == 0:
yield item
def multiply_by_ten(source):
"""乘以10"""
for item in source:
yield item * 10
def take_first_n(source, n):
"""取前n个"""
count = 0
for item in source:
if count >= n:
break
yield item
count += 1
# 链式调用
result = take_first_n(
multiply_by_ten(
filter_even(
source_generator()
)
),
5
)
return list(result)
# 测试链式生成器
print("链式生成器结果:", chained_generators())
常见陷阱与调试
生成器只能迭代一次
python
def single_use_generator():
"""生成器只能使用一次的演示"""
yield 1
yield 2
yield 3
# 错误使用方式
gen = single_use_generator()
print("第一次迭代:", list(gen))
print("第二次迭代:", list(gen)) # 空列表,生成器已耗尽
# 正确使用方式
def reusable_generator_func():
"""返回新的生成器实例"""
def generator():
yield 1
yield 2
yield 3
return generator()
# 每次调用都获得新的生成器
print("第一次使用:", list(reusable_generator_func()))
print("第二次使用:", list(reusable_generator_func()))
生成器与异常处理
python
def exception_handling_generator():
"""生成器中的异常处理"""
try:
yield 1
yield 2
# 这里可能会抛出异常
yield 3 / 0 # 故意制造异常
yield 4
except ZeroDivisionError as e:
print(f"生成器内部捕获异常: {e}")
yield f"错误处理结果: {e}"
finally:
print("生成器清理完成")
# 测试异常处理
gen = exception_handling_generator()
try:
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 触发异常处理
print(next(gen)) # 错误处理结果
except StopIteration:
print("生成器自然结束")
总结
生成器是Python中处理大数据和流式数据的强大工具。通过惰性求值,生成器能够:
- 节省内存:只在需要时生成数据
- 提高性能:避免不必要的计算
- 简化代码:用同步的方式编写异步逻辑
- 支持无限序列:处理理论上的无限数据流
掌握生成器的使用,能够让你的Python代码更加高效、优雅。在实际应用中,要根据具体场景选择合适的迭代方式,平衡内存使用和性能需求。
练习题
- 创建一个生成器,生成前n个三角数
- 使用生成器实现一个无限的随机游走序列
- 创建一个生成器管道,处理日志文件并提取错误信息
- 实现一个生成器版本的map函数
- 使用生成器实现一个内存高效的斐波那契数列生成器
记住:生成器不仅仅是一种语法特性,更是一种编程思维方式的转变------从"拥有数据"到"生成数据"。这种思维方式在处理现代大数据应用时尤为重要。
结尾
希望对初学者有帮助;致力于办公自动化的小小程序员一枚
希望能得到大家的【❤️一个免费关注❤️】感谢!
求个 🤞 关注 🤞 +❤️ 喜欢 ❤️ +👍 收藏 👍
此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏
此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏
此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏