14.Python的迭代器与生成器:懒加载与内存优化的艺术

49 阅读4分钟

@[toc]

Python的迭代器与生成器:懒加载与内存优化的艺术

迭代器与生成器是Python高效处理数据的核心工具,通过惰性计算(按需生成数据)大幅降低内存占用,尤其适合处理大型数据集、实时数据流等场景。本文将深入解析其原理,并通过实战案例教你灵活应用。


1. 迭代协议:__iter____next__

原理剖析
迭代器是实现了迭代器协议的对象,必须包含两个方法:

  • __iter__:返回迭代器自身(return self),使对象可被迭代。
  • __next__:返回下一个元素,无元素时抛出 StopIteration 异常,终止迭代。

实战:自定义倒计时迭代器

class Countdown:
    def __init__(self, start):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        num = self.current
        self.current -= 1
        return num

# 使用示例
for num in Countdown(5):
    print(num)  # 输出:5, 4, 3, 2, 1

关键特性

  • 一次性使用:遍历结束后需重新创建迭代器。
  • 内存高效:仅保存当前状态,不预加载所有数据。

💡 对比可迭代对象
列表、字典等是可迭代对象(实现 __iter__),但非迭代器(不实现 __next__)。迭代器一定是可迭代对象,反之不成立。


2. 生成器:yield与生成器表达式

原理:生成器是特殊的迭代器,通过 yield 暂停函数执行,下次调用时从暂停处继续,自动保存状态。

实战1:生成器函数(动态生成斐波那契数列)

def fibonacci(limit):
    a, b = 0, 1
    while a < limit:
        yield a
        a, b = b, a + b

# 生成小于100的斐波那契数
for num in fibonacci(100):
    print(num)  # 0, 1, 1, 2, 3, 5, 8...

实战2:生成器表达式(内存优化版列表推导)

# 生成1千万数据的平方(内存仅128B)
squares_gen = (x**2 for x in range(10_000_000))
print(next(squares_gen))  # 0

# 对比:列表推导占用约900MB内存
squares_list = [x**2 for x in range(10_000_000)]

高级技巧:生成器交互(send 方法)

def chat_bot():
    response = "Hello!"
    while True:
        msg = yield response  # 接收外部传入的值
        response = f"Received: {msg}"

bot = chat_bot()
next(bot)  # 初始化生成器
print(bot.send("Hi"))  # 输出:Received: Hi 

3. 实战:大文件分块读取与性能优化

场景:处理100GB日志文件,避免内存溢出。

方案1:逐行读取(文本文件)

def read_large_file(path):
    with open(path, "r") as f:
        for line in f:  # 逐行流式读取
            yield line.strip()

# 统计包含"ERROR"的行数
error_lines = (line for line in read_large_file("server.log") if "ERROR" in line)
error_count = sum(1 for _ in error_lines)  # 内存占用≈单行大小

方案2:分块读取(二进制文件)

def read_in_chunks(file_path, chunk_size=1024*1024):  # 1MB/块
    with open(file_path, "rb") as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk: 
                break
            yield chunk

# 处理每个数据块
for chunk in read_in_chunks("large_video.mp4"):
    process_chunk(chunk)  # 如加密/压缩

方案3:内存映射(随机访问大文件)

import mmap

with open("large_db.bin", "r+b") as f:
    with mmap.mmap(f.fileno(), 0) as mmap_obj:
        # 直接操作文件内存(无需加载全文)
        if mmap_obj.find(b"target_data") != -1:
            print("Data found!") 

性能对比

方法内存占用适用场景
逐行读取极低文本文件(如日志)
分块读取二进制文件(如图像)
内存映射需随机访问的大文件

迭代器 vs 生成器:核心区别总结

特性迭代器生成器
实现方式类中实现 __next__函数使用 yield
代码简洁性需完整定义类更简洁(自动保存状态)
内存占用低(仅当前状态)极低(动态生成)
适用场景自定义复杂遍历逻辑惰性计算、无限序列

下期预告:15.Python的装饰器与元编程:代码的“魔法外衣”与“自动化工厂”

内容亮点

  1. 装饰器原理:深入闭包与函数包装机制(例:@timer 统计函数耗时)。
  2. 🛠️ 元编程实战:动态创建类、拦截属性访问(__getattr__)。
  3. 🚀 应用场景
    • API权限校验装饰器
    • ORM框架的元类魔法
    • 自动注册插件系统

思考题:如何用装饰器实现函数执行耗时统计?答案下期揭晓!

掌握迭代器与生成器,你已迈入Python高效编程的大门。它们像数据的“流水线”,让海量处理举重若轻 🌟。

更多技术干货欢迎关注微信公众号“科威舟的AI笔记”~

【转载须知】:转载请注明原文出处及作者信息