生成器概述
生成器是 Python 中的一种特殊迭代器,通过普通函数的语法实现,但使用 yield
语句返回数据。生成器自动实现了 __iter__()
和 __next__()
方法,因此可以直接用于迭代。生成器的核心特点是延迟计算(lazy evaluation),即只在需要时生成下一个值,而不是一次性计算并存储所有值。
生成器的作用与优势
作用:
- 节省内存空间
- 按需生成数据项
- 支持无限序列生成
- 简化复杂迭代逻辑的代码
优势:
- 内存效率高,适用于大数据集
- 可以创建惰性求值的数据流
- 代码结构更加简洁易读
生成器的使用场景
场景 | 描述 |
---|---|
处理大数据集 | 当数据量非常大以至于无法全部加载到内存中时,生成器可以逐个生成数据项 |
创建无限序列 | 如自然数列、斐波那契数列等理论上没有终点的数据流 |
简化代码结构 | 在某些情况下,使用生成器可以让代码更加简洁、易维护 |
生成器表达式 vs 列表推导式
类型 | 语法 | 是否立即执行 | 示例 | 特点 |
---|---|---|---|---|
列表推导式 | 使用方括号 [] |
✅ 是 | [i * 5 for i in range(5)] |
立即计算结果并保存在内存中 |
生成器表达式 | 使用圆括号 () |
❌ 否 | (i * 5 for i in range(5)) |
延迟计算,每次迭代才会生成一个值 |
示例代码:
li = [i * 5 for i in range(5)]
print(li) # 输出: [0, 5, 10, 15, 20]
gen = (i * 5 for i in range(5))
print(gen) # 输出: <generator object ...>
print(gen.__next__()) # 输出: 0
生成器函数:yield 的作用
带有 yield
关键字的函数称为生成器函数。它不像普通函数那样返回一个值后就结束,而是可以在多个调用之间“暂停”和“恢复”。类似于中断函数。
yield 的特点:
- 每次调用
next()
会从上次yield
的位置继续执行 - 保留函数的状态
- 返回值不会被一次性计算出来,而是按需生成
示例代码:
def test():
yield 'a'
yield 'b'
yield 'c'
gen = test()
print(gen.__next__()) # 输出: a
print(gen.__next__()) # 输出: b
print(gen.__next__()) # 输出: c
可迭代对象、迭代器、生成器三者关系
名称 | 定义 | 特点 |
---|---|---|
可迭代对象(Iterable) | 实现了 __iter__() 方法的对象 |
可以用 for...in 遍历,如 list 、str 、dict 、迭代器、生成器 |
迭代器(Iterator) | 实现了 __next__() 方法的对象 |
可以使用 next() 获取下一个元素 |
生成器(Generator) | 一种特殊的迭代器,由 yield 函数或生成器表达式产生 |
自动实现 __iter__() 和 __next__() |
三者关系图示:
- 可迭代对象包含迭代器迭代器包含生成器
可迭代对象 ⊃ 迭代器 ⊃ 生成器
实战对比:列表 vs 生成器处理大数据
比较两种方式处理一千万个数字(0~9999999),并对每个数字进行平方操作。
使用模块:
time
:用于计时sys
:用于查看内存占用
代码如下:
import time
import sys
# 方法一:使用列表
def use_list():
start_time = time.time()
numbers = [i for i in range(10_000_000)] # 生成列表
squares = [x * x for x in numbers] # 计算平方
end_time = time.time()
print(f"【列表】耗时: {end_time - start_time:.4f} 秒")
print(f"【列表】占用内存: {sys.getsizeof(numbers) + sys.getsizeof(squares)} 字节")
# 方法二:使用生成器
def number_generator(n):
for i in range(n):
yield i
def use_generator():
start_time = time.time()
gen = number_generator(10_000_000)
squares = (x * x for x in gen) # 生成器表达式,不会立即计算
count = 0
for square in squares:
count += 1 # 强制执行生成器
end_time = time.time()
print(f"【生成器】耗时: {end_time - start_time:.4f} 秒")
print(f"【生成器】生成器对象本身占用内存: {sys.getsizeof(gen)} 字节")
# 运行测试
print("=== 开始测试 ===\n")
use_list()
print("\n------------------------\n")
use_generator()
print("\n=== 测试结束 ===")
结果分析(根据机器性能不同,数值可能略有差异):
指标 | 列表方式 | 生成器方式 |
---|---|---|
内存占用 | 非常大(约 184MB) | 极小(约 112B) |
耗时 | 略快 | 略慢 |
是否适合大数据 | ❌ 不适合 | ✅ 非常适合 |
总结
生成器是一种强大而高效的工具,特别适用于:
- 数据量庞大的场景
- 需要延迟加载的场景
- 需要节省内存的场景
- 需要简化复杂迭代逻辑的场景
虽然生成器在速度上略逊于列表,但它在内存使用上的优势使其成为处理大规模数据的首选方式。