Python 生成器与 yield 关键字实战:5 个节省内存的高级用法与性能优化技巧

Python 生成器与 yield 关键字实战:5 个节省内存的高级用法与性能优化技巧

在日常 Python 开发中,我们经常需要处理大量数据。如果把所有数据一次性加载到内存中,很容易导致内存溢出。生成器(Generator)正是解决这个问题的利器。

什么是生成器?

生成器是一种特殊的迭代器,它使用 yield 关键字按需产生值,而不是一次性生成所有结果。与普通函数不同,生成器函数在每次 yield 后会暂停执行,下次调用时从暂停的位置继续。

  1. 基础用法:读取大文件逐行处理

假设我们要分析一个 10GB 的日志文件,如果用列表存储所有行,内存直接爆掉。使用生成器可以逐行读取,内存占用始终保持在最低水平:

def read_large_file(file_path):

with open(file_path, 'r') as f:

for line in f:

yield line.strip()

for line in read_large_file('access.log'):

if 'ERROR' in line:

print(line)

这种方式的优势在于,无论文件有多大,内存中同时只存在一行数据。

  1. 生成器表达式:简洁的数据管道

生成器表达式用类似列表推导式的语法,但返回的是生成器对象而非列表,内存占用微乎其微:

numbers = range(10000000)

squared_even = (x * x for x in numbers if x % 2 == 0)

for val in squared_even:

if val > 1000:

break

print(val)

对比使用方括号的列表推导式,圆括号的生成器表达式不会预先创建整个列表,只在实际迭代时才计算每个值。

  1. yield from:组合多个生成器

Python 3.3 引入了 yield from 语法,可以将多个生成器串联起来,形成数据管道:

def read_config():

yield 'debug=True'

yield 'port=8080'

def read_data():

yield 'row1'

yield 'row2'

def pipeline():

yield from read_config()

yield '---'

yield from read_data()

for item in pipeline():

print(item)

这种模式非常适合构建 ETL 数据管道,每个阶段独立维护自己的逻辑。

  1. 生成器实现无限序列:斐波那契数列

生成器可以表示无限序列,因为值是按需计算的,不会占用无限内存:

def fibonacci():

a, b = 0, 1

while True:

yield a

a, b = b, a + b

fib = fibonacci()

for _ in range(10):

print(next(fib))

配合 itertools.islice 可以安全地取前 N 项,不用担心无限循环问题。

  1. 协程与生成器:send 方法实现双向通信

生成器不仅能产出值,还能通过 send 方法接收外部传入的数据,这是 Python 早期协程的基础:

def accumulator():

total = 0

while True:

value = yield total

if value is not None:

total += value

acc = accumulator()

next(acc) # 启动生成器

print(acc.send(10)) # 输出 10

print(acc.send(20)) # 输出 30

print(acc.send(5)) # 输出 35

这种模式可以用于实现流式计算、实时统计等场景。

  1. 性能对比:生成器 vs 列表

我们用一个简单的实验来量化生成器的内存优势:

import sys

import time

data = list(range(1000000))

列表方式

list_result = x \* 2 for x in data

print(f'列表占用内存: {sys.getsizeof(list_result)} 字节')

生成器方式

gen_result = (x * 2 for x in data)

print(f'生成器占用内存: {sys.getsizeof(gen_result)} 字节')

典型结果是列表占用约 8MB,而生成器仅占用不到 200 字节,差距超过 40000 倍。

  1. 实战案例:生成器实现生产者消费者模式

import threading

import queue

import time

def producer(q, n):

for i in range(n):

q.put(i)

time.sleep(0.1)

q.put(None) # 结束信号

def consumer(q):

while True:

item = q.get()

if item is None:

break

print(f'处理: {item}')

q = queue.Queue(maxsize=10)

t1 = threading.Thread(target=producer, args=(q, 20))

t2 = threading.Thread(target=consumer, args=(q,))

t1.start()

t2.start()

t1.join()

t2.join()

虽然这个例子用的是 queue,但生成器配合 yield 同样可以实现类似的生产者消费者模式,而且代码更加简洁优雅。

总结

生成器是 Python 中最被低估的特性之一。它的核心价值在于延迟计算和内存高效,特别适合以下场景:

  1. 处理大规模数据集,避免内存溢出

  2. 构建数据管道,实现流式处理

  3. 表示无限序列,按需生成

  4. 替代回调,简化异步代码逻辑

下次遇到需要处理大量数据的场景时,先想想能不能用生成器来优化,往往会有意想不到的效果。

相关推荐
用户83562907805115 小时前
Python 实现 PDF 文件加密与解密方法
后端·python
用户83562907805115 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
你好潘先生1 天前
别再记命令了,用 yeero do 说句人话就能跑脚本,而且不烧 token
服务器·python·命令行
Agent_大师1 天前
WebSocket 行情重连成功,K线缺口不会自动消失
python
荣码1 天前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
copyer_xyf1 天前
FastAPI 如何连接 MySQL
后端·python
apocelipes2 天前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
用户8356290780512 天前
使用 Python 在 PDF 中创建与管理书签
后端·python
MeixianAgent2 天前
Python 回测数据入口怎么验?历史 K 线入库前先做 5 个检查
后端·python
咕白m6252 天前
用 Python 实现一键批量查找与替换 Excel 数据
后端·python