Python脚本生成器如何节省内存

wen 实用脚本 1

本文目录导读:

Python脚本生成器如何节省内存

  1. 核心原理
  2. 主要节省内存的场景
  3. 实际节省内存测试
  4. 高级内存节省技巧
  5. 注意事项
  6. 何时使用生成器
  7. 性能权衡

Python脚本生成器通过惰性求值(Lazy Evaluation)来节省内存,与列表一次性加载所有数据不同,生成器只在需要时才生成下一个元素,因此占用的是常数量级的内存。

核心原理

对比示例

# 列表方式 - 一次性加载100万数据到内存
my_list = [x * 2 for x in range(1_000_000)]  # 约8MB内存
# 生成器方式 - 按需生成,仅占用几十字节
my_generator = (x * 2 for x in range(1_000_000))  # 约56字节

主要节省内存的场景

处理大文件时

# 错误方式:一次读入所有行
with open('large_file.txt') as f:
    lines = f.readlines()  # 全部加载到内存
# 正确方式:逐行读取(生成器行为)
with open('large_file.txt') as f:
    for line in f:  # f本身是生成器
        process(line)

大数据流处理

# 传统方式
def get_data():
    results = []
    for i in range(1_000_000):
        results.append(compute(i))
    return results
# 生成器方式
def get_data_generator():
    for i in range(1_000_000):
        yield compute(i)
# 使用生成器
for data in get_data_generator():
    process(data)

无限序列

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b
# 永远不会内存溢出
fib = fibonacci()
for _ in range(1000):
    print(next(fib))

实际节省内存测试

import sys
# 创建同等规模的列表和生成器
list_comprehension = [x for x in range(100_000)]
generator_expression = (x for x in range(100_000))
print(f"列表占用: {sys.getsizeof(list_comprehension)} bytes")
print(f"生成器占用: {sys.getsizeof(generator_expression)} bytes")
# 输出:
# 列表占用: 824464 bytes (约0.8MB)
# 生成器占用: 56 bytes

高级内存节省技巧

使用itertools

from itertools import islice, count, cycle
# islice:切片生成器,不创建中间列表
for item in islice(generator, 100):
    print(item)
# count:无限计数器
for i in islice(count(10), 5):
    print(i)  # 10, 11, 12, 13, 14

管道式处理

def read_file(filename):
    with open(filename) as f:
        for line in f:
            yield line.strip()
def filter_lines(lines):
    for line in lines:
        if 'ERROR' in line:
            yield line
def parse_line(lines):
    for line in lines:
        yield parse(line)
# 链式处理,全程零内存开销
for error in parse_line(filter_lines(read_file('large.log'))):
    report(error)

递归使用生成器

def tree_walk(node):
    yield node
    for child in node.children:
        yield from tree_walk(child)  # Python 3.3+

注意事项

不能重复使用

gen = (x for x in range(5))
print(list(gen))  # [0, 1, 2, 3, 4]
print(list(gen))  # []  已耗尽

需要重新生成

# 需要多次迭代时,需要重新创建生成器
for _ in range(3):
    for item in (x for x in range(5)):
        print(item, end=' ')
    print()
# 输出: 0 1 2 3 4 
#       0 1 2 3 4 
#       0 1 2 3 4

不能随机访问

gen = (x for x in range(5))
# gen[2]  # TypeError: 'generator' object is not subscriptable

何时使用生成器

场景 推荐方式
处理大文件 生成器
无限数据流 生成器
需要索引访问 列表
多次迭代 列表或重新创建生成器
数据量小(<1000) 两者均可

性能权衡

  • 内存节省:生成器完胜
  • CPU效率:生成器略快(减少了内存分配开销)
  • 灵活性:列表胜出(支持索引、切片等)

当处理大数据集、流式数据、不确定数据规模时,生成器是节省内存的最佳选择,对于小数据或需要频繁随机访问的场景,使用列表更合适。

抱歉,评论功能暂时关闭!