基于本文回答
0
评论

Ptyhon中的生成器(Generator)

知识点图片

Python 中的 生成器 (Generator) 是一种特殊的迭代器(Iterator)。

简单来说,生成器允许你一边循环一边计算,而不是一次性把所有结果计算出来存入内存。这使得它在处理大量数据或无限数据流时非常高效。

以下是关于生成器的详细解析:


1. 核心概念:为什么要用生成器?

假设你要处理 1 亿个数字。

  • 列表 (List):会一次性创建 1 亿个数字并存入内存,占用极大内存,甚至导致程序崩溃。
  • 生成器 (Generator):它不会一次性生成所有数字,而是保存算法。每次你需要一个数字时,它才计算一个给你。几乎不占内存

这种机制被称为 惰性求值 (Lazy Evaluation)


2. 如何创建生成器

有两种主要方式来创建生成器:

方法一:生成器表达式 (Generator Expression)

只要把列表推导式(List Comprehension)的 [] 换成 (),就变成了生成器。

python
# 列表推导式:立即生成所有数据
my_list = [x * x for x in range(5)]
print(my_list)  # 输出: [0, 1, 4, 9, 16]

# 生成器表达式:不立即生成
my_generator = (x * x for x in range(5))
print(my_generator)  # 输出: <generator object ... at 0x...>

# 需要通过循环或 next() 来取值
for num in my_generator:
    print(num)

方法二:生成器函数 (使用 yield 关键字)

如果一个函数中包含了 yield 关键字,那么这个函数就不再是一个普通函数,而是一个生成器函数。

yieldreturn 的区别:

  • return:返回结果并结束函数。
  • yield:返回结果并暂停函数,保留当前状态,等待下一次调用继续执行。
python
def simple_generator():
    print("Step 1")
    yield 1
    print("Step 2")
    yield 2
    print("Step 3")
    yield 3

# 创建生成器对象
gen = simple_generator()

# 执行
print(next(gen)) # 输出 "Step 1" 然后返回 1
print(next(gen)) # 输出 "Step 2" 然后返回 2
print(next(gen)) # 输出 "Step 3" 然后返回 3
# print(next(gen)) # 再次调用会抛出 StopIteration 异常

3. 实战案例:斐波那契数列

这是生成器最经典的用法。如果要生成前 N 个斐波那契数,使用列表可能会消耗大量内存,而生成器则非常轻量。

python
def fib(max_count):
    n, a, b = 0, 0, 1
    while n < max_count:
        yield b      # 返回当前的 b,并暂停在这里
        a, b = b, a + b
        n = n + 1
    return 'done'

# 使用 for 循环遍历生成器
for x in fib(6):
    print(x)

4. 生成器 vs 列表:内存对比

让我们看看处理大数据时两者的区别:

python
import sys

# 创建一个包含 100万 个数字的列表
list_data = [x for x in range(1000000)]
print(f"列表占用内存: {sys.getsizeof(list_data)} 字节")

# 创建一个包含 100万 个数字的生成器
gen_data = (x for x in range(1000000))
print(f"生成器占用内存: {sys.getsizeof(gen_data)} 字节")

典型输出结果:

  • 列表占用内存: ~8,448,728 字节 (约 8MB)
  • 生成器占用内存: ~112 字节 (极小,无论数据量多大,大小基本不变)

5. 进阶用法

next() 函数

生成器可以通过 next(gen) 来手动获取下一个值。当没有更多元素时,会抛出 StopIteration 异常。for 循环会自动捕获这个异常并停止循环。

yield from (Python 3.3+)

用于在一个生成器中调用另一个生成器(委托生成器)。常用于扁平化嵌套列表。

python
def sub_gen():
    yield 'A'
    yield 'B'

def main_gen():
    yield 1
    yield from sub_gen() # 相当于把 sub_gen 的值直接拿过来
    yield 2

for item in main_gen():
    print(item, end=' ')
# 输出: 1 A B 2

send() 方法

生成器不仅可以产出数据,还可以接收外部传进来的数据。

python
def interact():
    print("开始")
    val = yield 1  # 暂停,返回 1。下次恢复时,val 接收 send 发来的值
    print(f"接收到了: {val}")
    yield 2

g = interact()
print(next(g))      # 启动生成器,运行到 yield 1,输出 1
print(g.send("Hello")) # 发送 "Hello" 给 val,继续运行到 yield 2,输出 2

6. 总结

特性 列表 (List) 生成器 (Generator)
创建方式 [] 或普通函数 () 或带 yield 的函数
内存占用 高 (存所有数据) 低 (只存状态和算法)
计算方式 立即计算所有结果 惰性计算 (用一个算一个)
访问方式 索引访问 list[0] 只能顺序迭代 (nextfor)
适用场景 数据量小,需要频繁随机访问 数据量大,无限数据流,只需遍历一次

一句话总结: 如果你需要遍历一个很大的数据集,或者数据是流式的(无限的),请优先使用生成器。

右滑查看面试常问