Python高级特性:生成器完全指南

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()协程、返回值
适用场景 大数据集、流式处理、无限序列、管道处理

最佳实践

  1. ✅ 处理大文件时使用生成器逐行读取
  2. ✅ 需要无限序列时使用生成器
  3. ✅ 构建数据处理管道时使用生成器
  4. ✅ 需要节省内存时优先考虑生成器
  5. ⚠️ 需要随机访问或多次迭代时使用列表
  6. ⚠️ 对速度要求极高且数据量小时使用列表

生成器是Python中优雅而强大的工具,掌握它可以让你的代码更高效、更Pythonic!


💡 Python 学习不走弯路! 体系化实战路线:基础语法 · 异步Web开发 · 数据采集 · 计算机视觉 · NLP · 大模型RAG实战 ------ 全在「道满PythonAI」! 点赞 + 关注,持续更新干货!

📚 相关推荐阅读


如果这篇文章对你有帮助,欢迎点赞、评论、收藏,你的支持是我持续分享的动力!🎉

相关推荐
用户3754268434032 小时前
从零构建 Go IM 系统:WebSocket + AI Agent + gRPC 全栈实践
后端
技术爬爬虾2 小时前
OpenCode详细攻略,开源版Claude Code,免费模型与神级插件
前端·后端
邦爷的AI架构笔记2 小时前
GLM-5.1 接入踩坑记录:用免费开源模型搭个 AI 代码审计小工具
后端·算法
Bernard02152 小时前
我试了下最近很火的 Hermes Agent:真正值得看的,不是会调工具,而是会把经验沉淀成 Skill
前端·后端
之歆2 小时前
API 层架构设计 — 从 RESTful 到 GraphQL 的范式演进
vue.js·后端·restful·graphql
落木萧萧8252 小时前
从架构视角看 MyBatis Plus 的设计缺陷
后端
Moment2 小时前
AI全栈入门指南:使用 NestJs 创建第一个后端项目
前端·javascript·后端
希望永不加班2 小时前
SpringBoot 定时任务:@Scheduled 基础与动态定时
java·spring boot·后端·spring
我叫黑大帅2 小时前
如何设计应用层 ACK 来补充 TCP 的不足?
后端·面试·go