关于生成器的那些事儿

简介: 我猜大家对于生成器肯定并不陌生,但是为了能让我愉快的继续装逼,我们还是用点篇幅讲一下什么是生成器吧。比如在 Python 里,我们想生成一个范围 (1,100000) 的一个 list,于是我们无脑写了如下的代码出来 注1:这里有同学提出了为什么我们不直接返回 range(start,stop) ,Nice question,这里涉及到一个基础问题, range 的机制究竟是怎样的。

我猜大家对于生成器肯定并不陌生,但是为了能让我愉快的继续装逼,我们还是用点篇幅讲一下什么是生成器吧。比如在 Python 里,我们想生成一个范围 (1,100000) 的一个 list,于是我们无脑写了如下的代码出来


注1:这里有同学提出了为什么我们不直接返回 range(start,stop) ,Nice question,这里涉及到一个基础问题, range 的机制究竟是怎样的。这就要分版本而论了,在 Python 2.x 的版本中, range(start,stop)其实本质上是预先生成一个 list ,而 list 对象是一个 Iterator ,因此可以被 for 语句所使用。


然后在 Python 2.x 中还有一个语句叫做 xrange ,其生成的是一个 Generator 对象。


在 Python 3 中事情发生了一点变化,可能社区觉得 range 和 xrange 分裂太过蛋疼,于是将其合并,于是现在在 Python 3 中,取消了 xrange 的语法糖,然后 range 的机制也变成生成一个 Generator 而不是list


但是大家考虑过一个问题么,如果我们想生成数据量非常大,预先生成数据的行为无疑是很不明智的,这样会耗费大量的内存。于是 Python 给我们提供了一种新的姿势,Generator (生成器)。


是的,Generator 其中一个特性就是不是一次性生成数据,而是生成一个可迭代的对象,在迭代时,根据我们所写的逻辑来控制其启动时机。

Generator 深入

这里可能有一个问题,大家肯定想问 Python 开发者们不可能为了这一种使用场景而去单独创建一个 Generator 机制吧,那么我们 Generator 还有其余的使用场景么。当然,请看标题,对了嘛,Generator 另一个很大作用可以说就是当做协程使用。不过在这之前,我们要去深入的了解下 Generator 才能方便我们后面的讲解。

Generator 内建方法

关于 Python 中可迭代对象的一点背景知识

首先,我们来看看 Python 中的迭代过程。
在 Python 中迭代有两个概念,一个是 Iterable ,另一个是 Iterator 。让我们分别来看看。
第N次首先,Iterable 近似的可以理解成为一个协议,判断一个 Object 是否是 Iterable 的方法就是看其实现了 iter与否,如果实现了 iter ,那么这便可以认为是一个 Iterable 对象。空谈误国,实干兴邦,让我们直接来看一段代码理解下:


好了,让我们来看看上面这段代码里发生了什么,首先 for 语句的引用首先去判断迭代的是 Iterable 对象还是Iterator 对象,如果是实现了 __iter__ 方法的对象,那么就是一个 Iterable 对象, for 循环首先调用对象的__iter__ 方法来获取一个 Iterator 对象。那么什么是 Iterator 对象呢,这里可以近似的理解为是实现了 next() 方法(注:在Python3中是 next 方法)。

OK,让我们继续回到刚刚说到的那里,在上面的代码中 for 语句首先判断是一个 Iterable 对象还是 Iterator 对象,如果是 Iterable 对象那么调用其 iter 方法来获取一个 Iterator 对象,接着 for 循环会调用 Iterator 对象中的next() (注:Python3 里是 __next__ )方法来进行迭代,直到迭代过程结束抛出 StopIteration 异常。

聊聊 Generator 吧

让我们先看看前面那段代码吧:


首先我们要确定一点的是 Generator 其实也是一个 Iterator 对象。OK 让我们来看看上面这段代码,首先 for 确定 generateList1 是一个 Iterator 对象,然后开始调用 next() 方法进行进一步迭代。OK 此时你肯定想问这里面 next() 方法是怎样让 generateList1 进一步往下迭代的呢?答案在于 Generator 的内建 send() 方法。我们还是来看一段代码。


这里我们应该输出什么?答案就是 0,1,2,3,4 ,结果上和我们用 for 循环进行运算的结果是不是一样。好了,我们现在可以得出一个结论就是:

Generator 迭代的本质就是通过内建的 next() 或 __next__() 方法来调用内建的 send() 方法。

继续吐槽内建方法

前面我们提到一个结论 :

Generator 迭代的本质就是通过内建的 next() 或 __next__() 方法来调用内建的 send() 方法。

现在我们来看个例子:


好了这段代码的输出应该是什么?
答案是 [5,2,1,0] ,是不是很迷惑?别急,我们先来看看这段代码的运行流程

简而言之就是,当我们调用 send() 函数的时候,我们 send(x) 的值会发送给 newvalue 向下继续执行直到遇到下一次 yield 的出现,然后返回值作为一个过程的结束。然后我们的 Generator 静静的沉睡在内存中,等待下一
次的 send 来唤醒它。

注2:有同志问:“这里没想明白,c.send(3) 是 相当于 yield n 返回了个 3 给 newvalue ?”,好的,nice question,其实这个问题我们看前面之前的代码运行图就知道, c.send(3) 首先,将 3 赋值给 newvalue,然后程序运行剩下的代码,直到遇到下一个 yield 为止,那么在这里,我们运行剩下完代码,在遇到yiled n 之前,将 n 的值已经改变为 3 ,接着, yield n 即约等于 return 3 。接着 countdown 这个Generator 将所有变量的状态冻结,然后静静的呆在内存中,等待下一次的 next 或 __next__() 方法或者
是 send() 方法的唤醒。

小贴士:我们如果直接调用 send() 的话,第一次请务必 send(None) 只有这样一个 Generator 才算是真正被激活了。我们才能进行下一步操作。

说说关于协程
首先关于协程的定义,我们来看一段 wiki

Coroutines are computer program components that generalize subroutines for nonpreemptive multitasking,by allowing multiple entry points for suspending and resuming execution at certain locations. Coroutines are well­suited for implementing more familiar program components such as cooperative tasks, exceptions,event loop, iterators, infinite lists and pipes.
According to Donald Knuth, the term coroutine was coined by Melvin Conway in 1958, after he applied it to construction of an assembly program.[1] The first published explanation of the coroutine appeared later, in 1963.

简而言之,协程是比线程更为轻量的一种模型,我们可以自行控制启动与停止的时机。在 Python 中其实没有专门针对协程的这个概念,社区一般而言直接将 Generator 作为一种特殊的协程看待,想想,我们可以用 next 或__next__() 方法或者是 send() 方法唤醒我们的 Generator ,在运行完我们所规定的代码后, Generator 返回并将其所有状态冻结。这是不是很让我们 Excited 呢!!

课后作业

现在我们要后序遍历二叉树,我知道看这篇文章神犇们都能无脑写出来的,让我们看看代码先:


但是,我们知道递归深度太深的话,我们要么爆栈要么 py 交易失败,OK ,Generator 大法好,把你码农平安保。

还是直接看代码:



原文发布时间为:2016-11-17

本文作者:李者璈

本文来自云栖社区合作伙伴“Python中文社区”,了解相关信息可以关注“Python中文社区”微信公众号

相关文章
|
索引
如何避免 IDEA 每次重启都index
在 IntelliJ IDEA 中,可以通过以下几个步骤来避免每次重启时索引: 打开 File -> Settings 菜单。在左侧的菜单栏中选择 “Appearance & Behavior” -> “System Settings” -> “Synchronization”。
4611 0
|
20天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
8855 26
|
18天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
3736 16
|
18天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2234 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
5天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
404 1
|
13天前
|
人工智能 Linux 开发者
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
Codex是OpenAI推出的AI编程智能体,可读取本地项目、理解需求并自动修改代码。支持桌面GUI、命令行(CLI)及VS Code/Cursor插件三种形态,覆盖可视化操作、终端高效开发与编辑器无缝集成场景,助开发者用自然语言驱动编码全流程。(239字)
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
|
6天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
7天前
|
人工智能 Linux Windows
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
千问办公(QwenWork)是阿里云推出的AI智能办公平台,支持网页端直接使用及Windows/Mac/Linux客户端下载。提供PPT生成、财报分析、网页搭建等AI功能,个人版免费,企业版198元/席/月。详情见官网qwenwork.cn或阿里云产品页。
929 0
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面