来源:cococo点点 链接:www.cnblogs.com/coder2012/p/4990834.html 生成器
生成器是通过一个或多个yield表达式构成的函数,每一个生成器都是一个迭代器(但是迭代器不一定是生成器)。 如果一个函数包含yield关键字,这个函数就会变为一个生成器。 生成器并不会一次返回所有结果,而是每次遇到yield关键字后返回相应结果,并保留函数当前的运行状态,等待下一次的调用。 由于生成器也是一个迭代器,那么它就应该支持next方法来获取下一个值。 除了next函数,生成器还支持send函数。该函数可以向生成器传递参数。 最经典的例子,生成无限序列。 常规的解决方法是,生成一个满足要求的很大的列表,这个列表需要保存在内存中,很明显内存**了这个问题。 如果使用生成器就不需要返回整个列表,每次都只是返回一个数据,避免了内存的**问题。 生成器源码分析 生成器的源码在Objects/genobject.c。 调用栈 在解释生成器之前,需要讲解一下Python虚拟机的调用原理。 Python虚拟机有一个栈帧的调用栈,其中栈帧的是PyFrameObject,位于Include/frameobject.h。 栈帧保存了给出代码的的信息和上下文,其中包含最后执行的指令,全局和局部命名空间,异常状态等信息。f_valueblock保存了数据,b_blockstack保存了异常和循环控制方法。 举一个例子来说明, 那么,相应的调用栈如下,一个py文件,一个类,一个函数都是一个代码块,对应者一个Frame,保存着上下文环境以及字节码指令。 每一个栈帧都拥有自己的数据栈和block栈,独立的数据栈和block栈使得解释器可以中断和恢复栈帧(生成器正式利用这点)。 Python代码首先被编译为字节码,再由Python虚拟机来执行。一般来说,一条Python语句对应着多条字节码(由于每条字节码对应着一条C语句,而不是一个机器指令,所以不能按照字节码的数量来判断代码性能)。 调用dis模块可以分析字节码, 其中,
由了上面对于调用栈的理解,就可以很容易的明白生成器的具体实现。 生成器的源码位于object/genobject.c。 next与send函数,如下 从上面的代码中可以看到,send和next都是调用的同一函数gen_send_ex,区别在于是否带有参数。 PyEval_EvalFrameEx函数的功能为执行字节码并返回结果。 举一个例子,f_back上一个Frame,f_lasti上一次执行的指令的偏移量, 结果如下,其中第三行的英文为操作码,对应着上面的opcode,每次switch都是在不同的opcode之间进行选择。 回复下列数字即可获得相应干货下载: 1:一百多篇大数据文档下载! 2:超全数据分析资料免费下载!(包括SQL,R语言,SPSS,SAS,python,数据分析和数据挖掘) 3:清华大学数据科学院讲座内容集锦免费下载! 4:20G!超全数据分析 视频 教程免费下载!(包括R语言,SPSS,统计学基础,excel,数据挖掘,医学统计) 5:Python超全资料分享! 本文转载于微信公众号: 复旦大数据(FudanBigData),更多微信文章请扫描关注公众号: |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|