Python高级特性:生成器完全指南
按需生成,内存友好
前言
生成器(Generator)是Python中一种特殊的迭代器 ,它允许你按需生成值,而不是一次性生成所有值并存储在内存中。这在处理大数据集 或无限序列时特别有用。
本文将系统讲解生成器的概念、两种创建方式、高级特性、实际应用以及性能考量,帮助你掌握这一Python核心特性。
一、什么是生成器?
1.1 生成器的核心特点
| 特点 | 说明 |
|---|---|
| 惰性求值 | 按需生成值,不预先计算 |
| 节省内存 | 不需要存储所有元素 |
| 保持状态 | 可以暂停和恢复执行 |
| 一次性使用 | 只能迭代一次 |
| 语法简洁 | 类似函数,使用yield |
1.2 生成器 vs 普通列表
python
# 普通列表:一次性生成所有元素,占用大量内存
squares_list = [x**2 for x in range(1000000)]
print(f"列表内存: {squares_list.__sizeof__()} bytes") # ~8MB
# 生成器:按需生成元素,几乎不占内存
squares_gen = (x**2 for x in range(1000000))
print(f"生成器内存: {squares_gen.__sizeof__()} bytes") # ~104 bytes
# 生成器只在实际迭代时才计算值
for square in squares_gen:
if square > 100:
break
print(square, end=' ') # 0 1 4 9 16 25 36 49 64 81 100
📌 核心优势 :对于大数据集,生成器可以节省数百万倍的内存!
二、创建生成器的两种方法
2.1 生成器表达式
生成器表达式与列表推导式类似,但使用**圆括号()**而不是方括号[]:
python
# 列表推导式 - 立即生成所有元素
list_comp = [x**2 for x in range(10)]
print(f"列表推导式类型: {type(list_comp)}") # <class 'list'>
# 生成器表达式 - 按需生成元素
gen_exp = (x**2 for x in range(10))
print(f"生成器表达式类型: {type(gen_exp)}") # <class 'generator'>
# 使用生成器
for num in gen_exp:
print(num, end=' ') # 0 1 4 9 16 25 36 49 64 81
适用场景:简单的转换和过滤操作。
2.2 生成器函数
使用yield关键字定义的函数就是生成器函数:
python
def count_up_to(max):
"""计数生成器,生成1到max的数字"""
count = 1
while count <= max:
yield count # 暂停并返回值
count += 1
# 创建生成器对象
counter = count_up_to(5)
print(type(counter)) # <class 'generator'>
# 使用for循环迭代
for num in count_up_to(5):
print(num, end=' ') # 1 2 3 4 5
关键特性:
- 生成器函数在每次调用
next()时执行到下一个yield语句 - 然后暂停,保持所有局部变量的状态
- 下次调用时从暂停处继续执行
三、生成器的使用
3.1 使用for循环迭代(推荐)
python
def count_down(n):
while n > 0:
yield n
n -= 1
# 使用for循环自动处理StopIteration
for num in count_down(5):
print(num, end=' ') # 5 4 3 2 1
3.2 手动控制(使用next())
python
gen = count_up_to(3)
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
print(next(gen)) # 输出: 3
print(next(gen)) # 抛出StopIteration异常
# 安全的手动迭代
gen = count_up_to(3)
while True:
try:
value = next(gen)
print(value)
except StopIteration:
break
四、生成器的高级特性
4.1 生成器状态
生成器保持其执行状态,包括局部变量和指令指针:
python
def stateful_gen():
print("开始执行")
yield 1
print("继续执行")
yield 2
print("结束执行")
gen = stateful_gen()
print(next(gen)) # 输出"开始执行",返回1
print(next(gen)) # 输出"继续执行",返回2
print(next(gen)) # 输出"结束执行",抛出StopIteration
输出:
sql
开始执行
1
继续执行
2
结束执行
Traceback (most recent call last):
...
StopIteration
4.2 生成器返回值(Python 3.3+)
生成器可以使用return返回值,该值会在StopIteration异常中携带:
python
def gen_with_return():
yield 1
yield 2
return "完成"
gen = gen_with_return()
try:
while True:
print(next(gen))
except StopIteration as e:
print(f"返回值: {e.value}") # 输出: 返回值: 完成
4.3 生成器委托:yield from(Python 3.3+)
使用yield from可以委托给另一个生成器或可迭代对象:
python
def chain_generators(*iterables):
"""将多个可迭代对象串联起来"""
for it in iterables:
yield from it # 委托给子迭代器
# 示例:串联列表、元组、字符串
gen = chain_generators([1, 2], (3, 4), "ab")
result = list(gen)
print(result) # [1, 2, 3, 4, 'a', 'b']
# 等价于:
def manual_chain(*iterables):
for it in iterables:
for item in it:
yield item
4.4 生成器与协程:.send()方法
生成器也可以用于协程编程,通过.send()方法可以向生成器发送值:
python
def coroutine():
print("启动协程")
while True:
value = yield # 接收发送的值
print(f"接收到值: {value}")
co = coroutine()
next(co) # 启动协程(必须先调用next或send(None))
co.send(10) # 输出: "接收到值: 10"
co.send(20) # 输出: "接收到值: 20"
# 带返回值的协程
def calculator():
total = 0
while True:
value = yield total
if value is None:
break
total += value
return total
calc = calculator()
next(calc) # 启动
print(calc.send(10)) # 输出: 10
print(calc.send(20)) # 输出: 30
print(calc.send(30)) # 输出: 60
五、实际应用示例
5.1 斐波那契数列生成器
python
def fibonacci():
"""无限斐波那契数列生成器"""
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 获取前10个斐波那契数
fib = fibonacci()
fib_10 = [next(fib) for _ in range(10)]
print(fib_10) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# 获取小于100的所有斐波那契数
fib = fibonacci()
fib_under_100 = []
for num in fib:
if num >= 100:
break
fib_under_100.append(num)
print(fib_under_100) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
5.2 读取大文件(内存友好)
python
def read_large_file(file_path):
"""逐行读取大文件,不占用过多内存"""
with open(file_path, 'r', encoding='utf-8') as file:
for line in file:
yield line.strip() # 去除换行符
# 逐行处理大文件(如10GB的日志文件)
def process_line(line):
"""处理每一行(示例函数)"""
if "ERROR" in line:
print(f"发现错误: {line[:50]}...")
# 使用生成器处理,内存占用恒定
for line in read_large_file('huge_log_file.txt'):
process_line(line)
5.3 无限序列生成器
python
def natural_numbers(start=1):
"""生成自然数序列(无限)"""
n = start
while True:
yield n
n += 1
def even_numbers():
"""生成偶数序列(无限)"""
n = 0
while True:
yield n
n += 2
def prime_numbers():
"""生成素数序列(无限)"""
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
# 使用无限序列
primes = prime_numbers()
first_10_primes = [next(primes) for _ in range(10)]
print(first_10_primes) # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
5.4 数据处理管道
python
def read_lines(file_path):
"""读取文件行"""
with open(file_path, 'r') as f:
for line in f:
yield line.strip()
def filter_lines(lines, keyword):
"""过滤包含关键字的行"""
for line in lines:
if keyword in line:
yield line
def convert_to_uppercase(lines):
"""转换为大写"""
for line in lines:
yield line.upper()
def write_lines(lines, output_path):
"""写入文件"""
with open(output_path, 'w') as f:
for line in lines:
f.write(line + '\n')
# 构建处理管道
input_file = 'data.txt'
output_file = 'output.txt'
keyword = 'ERROR'
# 管道:读取 -> 过滤 -> 转换 -> 写入
lines = read_lines(input_file)
filtered = filter_lines(lines, keyword)
uppercased = convert_to_uppercase(filtered)
write_lines(uppercased, output_file)
print(f"处理完成,结果保存到 {output_file}")
六、练习题:杨辉三角生成器
python
def triangles():
"""杨辉三角生成器,逐行生成"""
row = [1]
while True:
yield row
# 计算下一行
row = [1] + [row[i] + row[i+1] for i in range(len(row)-1)] + [1]
# 测试:生成前10行杨辉三角
results = []
n = 0
for t in triangles():
results.append(t)
n += 1
if n == 10:
break
# 打印杨辉三角
for i, row in enumerate(results, 1):
# 格式化输出,使三角形居中显示
print(f"第{i:2d}行: {row}")
# 输出示例:
# 第 1行: [1]
# 第 2行: [1, 1]
# 第 3行: [1, 2, 1]
# 第 4行: [1, 3, 3, 1]
# 第 5行: [1, 4, 6, 4, 1]
# ...
七、性能考虑
7.1 内存效率对比
| 数据结构 | 内存占用 | 适用场景 |
|---|---|---|
| 列表 | 高(所有元素) | 小数据集、需要随机访问 |
| 生成器 | 极低(恒定) | 大数据集、流式处理 |
| 元组 | 中等 | 不可变数据 |
7.2 速度对比
python
import timeit
# 列表推导式
def test_list():
return [x**2 for x in range(10000)]
# 生成器表达式
def test_generator():
return (x**2 for x in range(10000))
# 求和性能测试
list_time = timeit.timeit('sum([x**2 for x in range(10000)])', number=1000)
gen_time = timeit.timeit('sum((x**2 for x in range(10000)))', number=1000)
print(f"列表求和: {list_time:.4f}秒")
print(f"生成器求和: {gen_time:.4f}秒")
# 生成器通常略慢(约10-20%),但内存占用小得多
7.3 一次性使用特性
python
# 生成器只能迭代一次
gen = (x**2 for x in range(5))
print(list(gen)) # [0, 1, 4, 9, 16]
print(list(gen)) # [] - 已经耗尽!
# 需要多次使用可以转换为列表或重新创建
values = list((x**2 for x in range(5))) # 转换为列表
print(values) # [0, 1, 4, 9, 16]
print(values) # [0, 1, 4, 9, 16] - 可以多次使用
八、总结
| 知识点 | 要点 |
|---|---|
| 定义 | 按需生成值的迭代器 |
| 创建方式1 | 生成器表达式:(expr for item in iterable) |
| 创建方式2 | 生成器函数:使用yield关键字 |
| 核心优势 | 节省内存、支持无限序列 |
| 限制 | 一次性使用、速度略慢 |
| 高级特性 | yield from委托、.send()协程、返回值 |
| 适用场景 | 大数据集、流式处理、无限序列、管道处理 |
最佳实践:
- ✅ 处理大文件时使用生成器逐行读取
- ✅ 需要无限序列时使用生成器
- ✅ 构建数据处理管道时使用生成器
- ✅ 需要节省内存时优先考虑生成器
- ⚠️ 需要随机访问或多次迭代时使用列表
- ⚠️ 对速度要求极高且数据量小时使用列表
生成器是Python中优雅而强大的工具,掌握它可以让你的代码更高效、更Pythonic!
💡 Python 学习不走弯路! 体系化实战路线:基础语法 · 异步Web开发 · 数据采集 · 计算机视觉 · NLP · 大模型RAG实战 ------ 全在「道满PythonAI」! 点赞 + 关注,持续更新干货!
📚 相关推荐阅读
- Python官方文档 - 生成器
- PEP 255 - Simple Generators
- PEP 342 - Coroutines via Enhanced Generators
- PEP 380 - Syntax for Delegating to a Subgenerator
如果这篇文章对你有帮助,欢迎点赞、评论、收藏,你的支持是我持续分享的动力!🎉