第76集:生成器函数

学习目标

  • 理解生成器函数的概念和作用
  • 掌握yield关键字的使用
  • 学会创建生成器函数
  • 理解生成器函数与普通函数的区别
  • 掌握生成器的执行流程
  • 学会使用yield from语句
  • 了解生成器的应用场景

什么是生成器函数

生成器函数(Generator Function)是一种特殊的函数,使用yield关键字来生成值。当调用生成器函数时,它不会立即执行函数体,而是返回一个生成器对象。

生成器函数的特点

  • 使用yield关键字而不是return
  • 每次调用next()时执行到yield语句
  • 保存执行状态,可以暂停和恢复
  • 自动实现迭代器协议
  • 内存效率高,惰性求值

基本语法

语法结构

def generator_function():
    yield value1
    yield value2
    # ...

与普通函数的对比

# 普通函数
def normal_function():
    return [1, 2, 3]

# 生成器函数
def generator_function():
    yield 1
    yield 2
    yield 3

基本示例

示例1:简单生成器函数

def simple_generator():
    """简单的生成器函数"""
    yield 1
    yield 2
    yield 3

# 创建生成器
gen = simple_generator()

# 获取值
print(next(gen))  # 1
print(next(gen))  # 2
print(next(gen))  # 3

示例2:使用for循环遍历

def simple_generator():
    yield 1
    yield 2
    yield 3

# 使用for循环
for num in simple_generator():
    print(num)

示例3:带参数的生成器函数

def count_generator(n):
    """生成0到n-1的数字"""
    for i in range(n):
        yield i

# 使用生成器
for num in count_generator(5):
    print(num)

生成器的执行流程

执行机制

def execution_demo():
    print("开始执行")
    yield 1
    print("继续执行")
    yield 2
    print("再次执行")
    yield 3
    print("执行结束")

# 创建生成器
gen = execution_demo()

# 第一次调用next()
print("第一次next():")
result = next(gen)  # 输出: 开始执行
print(f"返回值: {result}")  # 返回值: 1

# 第二次调用next()
print("\n第二次next():")
result = next(gen)  # 输出: 继续执行
print(f"返回值: {result}")  # 返回值: 2

# 第三次调用next()
print("\n第三次next():")
result = next(gen)  # 输出: 再次执行
print(f"返回值: {result}")  # 返回值: 3

执行流程图

  1. 调用生成器函数,返回生成器对象(不执行函数体)
  2. 第一次调用next(),开始执行函数体,遇到yield暂停
  3. 第二次调用next(),从上次暂停处继续执行,遇到下一个yield暂停
  4. 重复步骤3,直到函数结束或遇到return
  5. 函数结束时抛出StopIteration异常

yield vs return

区别对比

# 使用return的函数
def return_function():
    return [1, 2, 3]
    return [4, 5, 6]  # 永远不会执行

# 使用yield的生成器函数
def yield_function():
    yield 1
    yield 2
    yield 3
    yield 4
    yield 5
    yield 6

关键区别

特性 return yield
执行次数 一次 多次
返回值 单个值或对象 多个值(逐个生成)
函数状态 不保存 保存
内存使用 一次性生成 惰性生成

生成器函数的应用

应用1:斐波那契数列

def fibonacci(n):
    """生成前n个斐波那契数"""
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

# 使用斐波那契生成器
for num in fibonacci(10):
    print(num)

应用2:无限序列

def infinite_counter(start=0):
    """无限计数器"""
    while True:
        yield start
        start += 1

# 使用无限计数器(需要手动停止)
counter = infinite_counter()
for i, num in enumerate(counter):
    print(num)
    if i >= 10:
        break

应用3:逐行读取文件

def read_file_lines(filename):
    """逐行读取文件"""
    with open(filename, 'r', encoding='utf-8') as f:
        for line in f:
            yield line.strip()

# 使用文件行生成器
for line in read_file_lines('example.txt'):
    print(line)

应用4:数据批处理

def batch_processor(data, batch_size):
    """将数据分批处理"""
    for i in range(0, len(data), batch_size):
        yield data[i:i + batch_size]

# 使用批处理生成器
data = list(range(20))
for batch in batch_processor(data, 5):
    print(f"批次: {batch}")

yield from 语句

基本语法

def sub_generator():
    yield 1
    yield 2

def main_generator():
    yield from sub_generator()
    yield 3

示例5:使用yield from

def sub_generator():
    """子生成器"""
    yield "A"
    yield "B"
    yield "C"

def main_generator():
    """主生成器"""
    yield from sub_generator()
    yield "D"
    yield "E"

# 使用生成器
for item in main_generator():
    print(item)
# 输出: A, B, C, D, E

示例6:嵌套生成器

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]
for item in flatten(nested):
    print(item)
# 输出: 1, 2, 3, 4, 5, 6, 7

生成器的高级用法

示例7:带状态的生成器

def stateful_generator():
    """带状态的生成器"""
    state = 0
    while True:
        received = yield state
        if received is not None:
            state = received
        else:
            state += 1

# 使用带状态的生成器
gen = stateful_generator()
print(next(gen))  # 0
print(next(gen))  # 1
print(gen.send(10))  # 10
print(next(gen))  # 11

示例8:管道处理

def source():
    """数据源"""
    for i in range(10):
        yield i

def filter_even(numbers):
    """过滤偶数"""
    for num in numbers:
        if num % 2 == 0:
            yield num

def square(numbers):
    """平方"""
    for num in numbers:
        yield num ** 2

# 使用管道处理
pipeline = square(filter_even(source()))
for result in pipeline:
    print(result)

示例9:生成器表达式转生成器函数

# 生成器表达式
gen_exp = (x ** 2 for x in range(10))

# 等价的生成器函数
def gen_func():
    for x in range(10):
        yield x ** 2

生成器的优势

优势1:内存效率

# 列表方式 - 占用大量内存
def get_squares_list(n):
    return [x ** 2 for x in range(n)]

# 生成器方式 - 内存占用小
def get_squares_generator(n):
    for x in range(n):
        yield x ** 2

优势2:惰性求值

def lazy_evaluation():
    """惰性求值示例"""
    print("生成第一个值")
    yield 1
    print("生成第二个值")
    yield 2
    print("生成第三个值")
    yield 3

# 只在需要时才计算
gen = lazy_evaluation()
print("创建生成器")
next(gen)  # 此时才输出: 生成第一个值

优势3:无限序列

def infinite_sequence():
    """无限序列"""
    num = 0
    while True:
        yield num
        num += 1

# 可以生成无限多个值
gen = infinite_sequence()
for i in range(5):
    print(next(gen))

生成器的限制

限制1:只能遍历一次

def my_generator():
    yield 1
    yield 2
    yield 3

gen = my_generator()

# 第一次遍历
for num in gen:
    print(num)

# 第二次遍历 - 不会输出
for num in gen:
    print(num)

限制2:不能回退

def my_generator():
    yield 1
    yield 2
    yield 3

gen = my_generator()
next(gen)  # 1
next(gen)  # 2
# 无法回到1

最佳实践

实践1:大数据处理使用生成器

# 推荐:使用生成器处理大数据
def process_large_data(data):
    for item in data:
        processed = transform(item)
        yield processed

# 不推荐:使用列表
def process_large_data(data):
    result = []
    for item in data:
        processed = transform(item)
        result.append(processed)
    return result

实践2:使用yield from简化代码

# 推荐:使用yield from
def main_generator():
    yield from sub_generator()

# 不推荐:手动遍历
def main_generator():
    for item in sub_generator():
        yield item

实践3:生成器函数命名

# 推荐:生成器函数名包含"generator"或使用复数形式
def numbers_generator():
    yield 1
    yield 2

def get_numbers():
    yield 1
    yield 2

实践4:正确处理StopIteration

def safe_generator():
    """安全的生成器"""
    try:
        yield 1
        yield 2
        yield 3
    except GeneratorExit:
        print("生成器被关闭")

常见错误

错误1:在生成器中使用return

# 错误:在生成器中使用return返回值
def bad_generator():
    yield 1
    yield 2
    return [3, 4, 5]  # 这个值不会被返回

# 正确:使用yield返回所有值
def good_generator():
    yield 1
    yield 2
    yield 3
    yield 4
    yield 5

错误2:忘记yield关键字

# 错误:忘记yield关键字
def not_a_generator():
    for i in range(10):
        print(i)

# 正确:使用yield
def is_a_generator():
    for i in range(10):
        yield i

错误3:在生成器中使用break

# 错误:在生成器中使用break
def bad_generator():
    for i in range(10):
        if i > 5:
            break  # 生成器会提前结束
        yield i

# 正确:使用return或条件判断
def good_generator():
    for i in range(10):
        if i <= 5:
            yield i

性能优化

优化1:避免不必要的计算

# 优化前:计算所有值
def all_squares(n):
    return [x ** 2 for x in range(n)]

# 优化后:按需计算
def lazy_squares(n):
    for x in range(n):
        yield x ** 2

优化2:使用生成器表达式

# 优化前:使用生成器函数
def simple_gen(n):
    for x in range(n):
        yield x

# 优化后:使用生成器表达式
simple_gen = (x for x in range(n))

总结

生成器函数是Python中强大的工具,具有以下特点:

核心概念

  • 使用yield关键字生成值
  • 保存执行状态,可以暂停和恢复
  • 自动实现迭代器协议
  • 惰性求值,按需生成值

优势

  • 内存效率高
  • 适合处理大数据
  • 可以生成无限序列
  • 代码简洁优雅

应用场景

  • 大数据处理
  • 流式处理
  • 无限序列
  • 数据管道

掌握生成器函数,将帮助你编写更高效、更优雅的Python代码。生成器函数是Python中重要的概念,理解它的工作原理对于编写高效的Python程序至关重要。

« 上一篇 迭代器与可迭代对象 下一篇 » 装饰器基础