← 返回首页

生成器深入

📂 python ⏱ 2 min 291 words

生成器深入

生成器是Python中一种特殊的迭代器,它使用 yield 关键字返回值,而不是 return。生成器的核心优势在于惰性求值——只在需要时才计算下一个值,极大地节省内存。

yield 的基本用法

def countdown(n):
    """倒计时生成器"""
    print("开始倒计时!")
    while n > 0:
        yield n
        n -= 1
    print("发射!")

for num in countdown(5):
    print(num)
# 输出: 5, 4, 3, 2, 1, 发射!

yield 会暂停函数执行并返回值,下次迭代时从上次暂停的位置继续。

生成器表达式

类似列表推导式,但使用圆括号:

# 列表推导式 - 一次性创建所有元素
squares_list = [x**2 for x in range(1000000)]

# 生成器表达式 - 按需生成
squares_gen = (x**2 for x in range(1000000))

print(type(squares_gen))  # <class 'generator'>

yield from 语法

yield from 用于将一个可迭代对象的值逐个yield出来:

def flatten(nested_list):
    """递归展平嵌套列表"""
    for item in nested_list:
        if isinstance(item, list):
            yield from flatten(item)  # 委托给子生成器
        else:
            yield item

nested = [1, [2, 3], [4, [5, 6]], 7]
print(list(flatten(nested)))
# [1, 2, 3, 4, 5, 6, 7]

send 方法

生成器可以通过 send() 方法接收外部传入的值:

def accumulator():
    """累加器生成器"""
    total = 0
    while True:
        value = yield total
        if value is None:
            break
        total += value

acc = accumulator()
next(acc)          # 启动生成器
print(acc.send(10))  # 10
print(acc.send(20))  # 30
print(acc.send(5))   # 35

生成器的状态

生成器有三种状态:GEN_CREATED(已创建)、GEN_SUSPENDED(挂起)、GEN_CLOSED(已关闭):

def simple_gen():
    yield 1
    yield 2
    yield 3

gen = simple_gen()
print(gen.gi_code.co_flags)  # 生成器状态

# 使用 inspect 模块检查状态
import inspect
print(inspect.getgeneratorstate(gen))  # GEN_CREATED
next(gen)
print(inspect.getgeneratorstate(gen))  # GEN_SUSPENDED

异常处理

生成器可以捕获外部抛入的异常:

def generator_with_exception():
    try:
        while True:
            value = yield
            print(f"收到: {value}")
    except ValueError as e:
        print(f"捕获异常: {e}")
    finally:
        print("生成器关闭")

gen = generator_with_exception()
next(gen)
gen.send(10)       # 收到: 10
gen.throw(ValueError, "测试错误")  # 捕获异常: 测试错误

无限序列

生成器天然适合表示无限序列:

def fibonacci():
    """无限斐波那契数列"""
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 使用 islice 获取前10个
from itertools import islice
fib = fibonacci()
print(list(islice(fib, 10)))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

实战:管道式数据处理

def read_data(filename):
    with open(filename) 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 transform(lines):
    for line in lines:
        yield line.upper()

# 管道组合
data = read_data("data.txt")
filtered = filter_lines(data, "error")
uppercased = transform(filtered)

for line in uppercased:
    print(line)

生成器是Python中处理大数据和流式处理的利器,掌握它能让你写出更高效的代码。