首页 存档 技术 查看内容

Python yield与实现

2018-3-30 13:00 |来自: 互联网 318 0

摘要: 来源:cococo点点 链接:www.cnblogs.com/coder2012/p/4990834.html 1生成器 生成器是通过一个或多个yield表达式构成的函数,每一个生成器都是一个迭代器(但是迭代器不一定是生成器)。 如果一个函数包含yield ...


来源:cococo点点

链接:www.cnblogs.com/coder2012/p/4990834.html



1

生成器

生成器是通过一个或多个yield表达式构成的函数,每一个生成器都是一个迭代器(但是迭代器不一定是生成器)。


如果一个函数包含yield关键字,这个函数就会变为一个生成器。


生成器并不会一次返回所有结果,而是每次遇到yield关键字后返回相应结果,并保留函数当前的运行状态,等待下一次的调用。


由于生成器也是一个迭代器,那么它就应该支持next方法来获取下一个值。


基本操作




除了next函数,生成器还支持send函数。该函数可以向生成器传递参数。



应用


最经典的例子,生成无限序列。


常规的解决方法是,生成一个满足要求的很大的列表,这个列表需要保存在内存中,很明显内存**了这个问题。



如果使用生成器就不需要返回整个列表,每次都只是返回一个数据,避免了内存的**问题。




2

生成器源码分析


生成器的源码在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。


生成器的创建



send与next


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清华大学数据科学院讲座内容集锦免费下载!

420G!超全数据分析 视频 教程免费下载!(包括R语言,SPSS,统计学基础,excel,数据挖掘,医学统计)

5Python超全资料分享!


本文转载于微信公众号: 复旦大数据(FudanBigData),更多微信文章请扫描关注公众号:

声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部