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. 替代回调,简化异步代码逻辑

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

相关推荐
starvapour7 小时前
CUDA_VISIBLE_DEVICES与nvidia-smi显卡序号不一致的问题
python·ai·cuda
码界筑梦坊7 小时前
112-基于Flask的游戏行业销售数据可视化分析系统
开发语言·python·游戏·信息可视化·flask·毕业设计·echarts
(Charon)7 小时前
【C++/Qt】Qt 实现 POP3/IMAP 邮件测试工具:连接邮箱服务器、登录与读取邮件
服务器·开发语言·c++
时空系7 小时前
第12篇:文档操作——文件读写 python中文编程
开发语言·python·ai编程
枫叶丹47 小时前
【HarmonyOS 6.0】Camera Kit 新增系统性能压力监听功能全解析
开发语言·计算机视觉·华为·harmonyos
十五年专注C++开发7 小时前
QtnProperty:一个基于 Qt 框架的第三方高级属性库
开发语言·c++·qt
2501_901006477 小时前
Golang map底层实现原理_Golang map哈希表原理教程【收藏】
jvm·数据库·python
yujunl7 小时前
U9的OpenAPI接口的应用
开发语言
keineahnung23457 小时前
為什麼這個 Tensor 算 dense?PyTorch _eval_is_non_overlapping_and_dense 深入解析
人工智能·pytorch·python·深度学习