那天吃完午饭,我正靠在工位上打着瞌睡,突然我们组的实习生跑过来问我一句:“哥,Python 的 generator 到底是干嘛的?我看文档上写能节省内存,但我一点没体会到啊。”我一听这个问题,瞬间清醒了。因为这事儿我当年也懵过,直到有一次线上日志炸了,才算真理解了生成器的妙处。
先这么说吧,generator 就是 Python 里一个能“边干活边产出结果”的家伙。你平时写的函数,一般都是“全做完再一次性返回结果”,比如:
这种函数看起来挺正常的,但如果你打印下 sys.getsizeof(get_numbers()),那内存直接飙上去。因为它会在内存里一下子创建出一百万个数字。那要是再大点,比如十亿个?电脑直接卡死。
而生成器不一样。它不是一次性造出所有结果,而是“你要一个我给一个”,就像外卖骑手:你点一份我送一份,不会一次性全堆门口。你写成这样:
关键字 yield 就是生成器的灵魂,它会让函数变成一种“可迭代对象”,但不会立刻执行完。当你去遍历它的时候,它才慢慢“吐出”结果。内存瞬间轻松了,系统也不会崩。
我还记得当时是怎么真切体会到 generator 的威力的。那天线上跑一个日志分析脚本,里面要读取几十个 GB 的日志文件。我写的那版是直接 readlines() 一次读进内存,结果整个服务器直接 OOM。我被领导点名复盘的时候脸都绿了。后来同事教我用生成器改写:
这段代码看起来没啥特别的,但它神奇的地方就在这里——文件是逐行读的,不会一次性塞到内存。整个脚本从十几分钟卡死,变成稳定跑完几小时,内存占用才几十 MB。那次之后我彻底信了 generator 的魔法。
那你可能会问了,除了省内存,还有啥好处?其实还有两个很实用的点:一个是提高性能,另一个是简化异步逻辑。
先说性能。举个例子,比如你要找出一个列表中所有偶数的平方。如果用列表推导式:
这没问题,但会立刻创建一个完整的列表。而生成器表达式就像是它的“懒加载版本”:
区别就在一个小括号。这个版本不会立刻生成全部数据,只有当你遍历它时才会逐步算出来。对于大型数据处理,这个差距是质变的。
我那会儿写日志聚合分析时,就是用这种方式。以前整份日志文件全进内存,现在一行一行过滤、处理、统计,用生成器串起来,代码可读性高、性能还稳。
再说异步。其实 Python 的协程(async/await)机制就是从生成器演化来的。早期版本还没有协程语法时,就是用 yield 来实现“暂停和恢复”的。比如:
这个 yield 本质上可以理解为一个“暂停点”。执行到那一步时,它会保存当前函数状态,下次再调用时,从这个状态继续往下走。这不就是协程干的事嘛?所以后来才有了更高级的 async def 写法,但本质思路一脉相承。很多框架(比如 Tornado、早期的 asyncio)底层都靠生成器实现异步调度。
不过说句实话,generator 也不是万能的。我刚接触的时候,特别容易写出一堆乱七八糟的 yield 函数,自己都不知道状态在哪。比如这个:
当你用 next() 调用它的时候,它不是一次性执行,而是每次执行到 yield 停下来。比如:
输出依次是:
它其实就像是函数的“断点续传”。每次 yield 都会暂停执行,等下次再恢复。如果你脑子不太清楚逻辑,调试起来就跟捉迷藏一样。尤其是在多层嵌套生成器的时候,真的容易把自己绕晕。
我还记得有次写爬虫的时候,用生成器嵌套生成器,一个负责抓页面,一个负责解析链接,还有一个控制调度。结果我当时加了个错误重试的逻辑,yield 嵌进 try-except 里,异常一出来整个生成链断了。那会儿我差点怀疑人生。后来才学会用 yield from 把子生成器“代理”出来,这样结构清晰多了:
这句 yield from 等价于把 fetch_page 里的所有 yield 都打平出来,既省代码又逻辑顺滑。也是 Python 3 之后才支持的一个小亮点。
讲到这其实差不多该总结一下了。generator 这个东西,简单说就是:它能让你在需要时才产出数据,不浪费内存,还能天然支持异步逻辑。它的本质是“惰性求值”——你不去取,它就不计算。听着简单,但真正灵活运用起来,能让代码又快又优雅。
尤其在处理流式数据(文件、日志、网络请求)时,生成器几乎是唯一正确的选择。以前你可能要写一堆缓存、分页逻辑,现在一句 yield 就搞定。节约资源的同时,还让代码更贴近“数据流”的思维。
后来那个实习生也终于明白了,过来和我说:“原来 generator 就是个‘边走边算’的函数啊。”我笑着说,对,它像个“节奏掌控者”,不是一口气冲刺,而是每一步都精准输出。其实编程很多时候就是这样——不是要让电脑做得更多,而是让它做得刚刚好。
所以啊,Python 的生成器看似平淡,背后藏着效率的哲学。你要真懂了它,就能写出更轻盈、更可控的程序。再也不会因为一个几百 MB 的文件把内存挤爆,也不会被“等所有结果算完再返回”的老逻辑拖垮。那天我跟实习生说完这话,转头自己想想,也挺感慨的——学技术这事,很多时候就是得自己卡过一两次,才会真心服。
以上就是“什么是 Python 中的生成器(generator)?你为什么会使用它?”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料

- 本文固定链接: http://www.phpxs.com/post/13591/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料