Python中迭代器和生成器:让数据“懒”得刚刚好 💤

"聪明的程序员不是算得更快,而是算得更少 。"

------ 迭代器与生成器,正是 Python 践行这一哲学的优雅体现。

问题引入

你有没有遇到过这样的场景?

  • 想遍历一个超大文件,但内存爆了 ❌
  • 写了个 range(10**9),结果电脑卡死 🐌
  • 面试被问:"迭代器和生成器有啥区别?" 瞬间语塞 😅

别慌!今天我们就把这两个看似高冷的概念,用一杯咖啡的时间讲清楚 ☕️。它们不是魔法,而是延迟计算(Lazy Evaluation) 的实用工具------只在你需要时才干活,绝不提前内卷!


核心剖析

迭代器(Iterator):会"一步一步走"的对象

在 Python 中,迭代器 是一个实现了 __iter__()__next__() 方法的对象。它遵循迭代协议

  • __iter__() 返回自身(支持 for 循环)
  • __next__() 返回下一个值,若无则抛出 StopIteration
python 复制代码
class Countdown:
    def __init__(self, start):
        self.start = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.start <= 0:
            raise StopIteration
        self.start -= 1
        return self.start + 1

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

💡 所有可迭代对象(如 list、dict)本身不是迭代器 ,但可以通过 iter() 转成迭代器。


生成器(Generator):写起来像函数,用起来像迭代器

生成器 是创建迭代器的"快捷方式"。你只需在函数中用 yield 替代 return,Python 自动帮你实现迭代协议!

python 复制代码
def countdown_gen(n):
    while n > 0:
        yield n
        n -= 1

# 使用
for num in countdown_gen(3):
    print(num)  # 输出: 3, 2, 1

关键区别来了👇:

特性 迭代器 生成器
创建方式 手写类 + __iter__/__next__ 函数 + yield
内存占用 手动控制 自动优化,极低
可读性 较繁琐 极简清晰 ✅
适用场景 复杂状态管理 流式数据、无限序列

🚀 生成器本质是语法糖,但它甜得恰到好处!


动手实践:处理大文件不崩内存

假设你有一个 10GB 的日志文件,想逐行读取并过滤错误信息:

python 复制代码
def read_large_file(file_path):
    """生成器:按需读取,不加载全文件到内存"""
    with open(file_path, 'r') as f:
        for line in f:
            if 'ERROR' in line:
                yield line.strip()

# 使用(即使文件巨大,内存也稳如老狗)
for error_line in read_large_file('app.log'):
    print(error_line)

对比暴力做法 lines = open('app.log').readlines() ------ 后者可能直接 OOM(Out of Memory)!


避坑指南 ⚠️

  1. 生成器只能遍历一次!

    python 复制代码
    gen = (x for x in range(3))
    list(gen)  # [0, 1, 2]
    list(gen)  # [] ← 已耗尽!

    解决方案:需要多次使用?转成 list,或重新调用生成器函数。

  2. 别混淆"可迭代对象"和"迭代器"

    python 复制代码
    lst = [1, 2, 3]
    iter(lst) is iter(lst)  # False!每次返回新迭代器
    it = iter(lst)
    iter(it) is it          # True!迭代器的 __iter__ 返回自己
  3. 生成器表达式 vs 列表推导式

    python 复制代码
    # 列表推导式:立即计算,占内存
    squares_list = [x**2 for x in range(1000000)]
    
    # 生成器表达式:延迟计算,省内存
    squares_gen = (x**2 for x in range(1000000))

延伸思考

  • yield from 是什么?

    它用于"委托"另一个生成器,避免嵌套 for 循环。比如合并多个生成器流。

  • 生成器能接收外部数据吗?

    可以!通过 generator.send(value) 实现双向通信,常用于协程(async/await 的底层基础之一)。

  • 为什么 range() 不是生成器?

    因为它是可重复迭代的序列对象 ,且支持 len()、索引等操作------生成器做不到这些。


小结 & 行动号召 💡

  • 迭代器:协议驱动,手动实现,灵活但啰嗦
  • 生成器yield 一行搞定,内存友好,开发首选

下次当你需要处理"大量数据""无限序列"或"流式输入"时,记得问自己:
"我能用生成器让它'懒'一点吗?"

试试改写你项目中的某个循环,用生成器替代列表推导式,观察内存变化吧!欢迎在评论区分享你的"懒人优化"成果~ 🧪✨

相关推荐
木昆子18 小时前
几种AI Agent开发框架对比:相比手写代码是否更便捷?
人工智能·后端·python
进阶的鱼19 小时前
(源码+实例)大家都在说Agent,那么Agent到底是什么?
python·llm·agent
bosins19 小时前
基于Python实现PDF转图片工具
开发语言·python·pdf
木昆子19 小时前
AI Agent案例实践:智能体开发模式详解之三(基于QwenAgent框架)
人工智能·后端·python
2501_9418663719 小时前
面向微服务配置动态更新与灰度发布的互联网系统高可用设计与多语言工程实践分享
java·运维·python
叫我:松哥19 小时前
基于Flask的智能服装搭配推荐系统,采用协同过滤和内容过滤的混合推荐算法,支持虚拟试穿和个性化建议
人工智能·python·算法·信息可视化·flask·scikit-learn·推荐算法
春日见19 小时前
强化学习第一讲:强化学习是什么,强化学习分类
开发语言·jvm·人工智能·python·学习·matlab·强化学习
郝学胜-神的一滴19 小时前
文海撷英,数林建模:词袋模型之奥义与中文处理实践
人工智能·python·程序人生·ai·自然语言处理·scikit-learn
2501_9418204919 小时前
从权限失控到最小授权模型落地的互联网系统安全工程实践随笔与多语言语法思考
开发语言·python
2501_9414043119 小时前
面向微服务分布式限流与熔断保护的互联网系统高可用设计与多语言工程实践分享
python