Python生成器源码剖析
生成器是个什么鬼?
生成器(Generator)在python2.3时成为python的标准特性,因此也多加了一个yield的关键字.(是的,就是java线程让步的那个yield).生成器最神奇的特性就是: 一个函数可以返回多次结果,而不是像普通函数一样只返回一次.(神不神奇,惊不惊喜~)
普通的python函数内部, 加个yield关键字, python解析器就将该函数视为一个生成器函数. 但是生成器函数不是生成器本身,而是生成器工厂.所以调用一个生成器函数时, 将创建一个生成器对象. 当外部需要从这个生成器获取值时,生成器会通过yield返回值,而非普通函数的return方法.这个过程中, yield偷偷做了两件事:
- 将值返回给调用方
- 标记当前执行位置, 当生成器再运行时,从标记位置恢复运行
说了这么多,可以上代码了
def return_a_generator(): # 这货是个生成器函数
yield 'foobar'
yield 42
yield 'hello'
复制代码
generator = return_a_generator() #这步操作只是为了产生生成器对象, 也可以称为激活
复制代码
next(generator) # 真二八经的第一次调用,next就是一个调用方
复制代码
'foobar'
复制代码
next(generator) # 我还可以被调用哦
复制代码
42
复制代码
next(generator) # 这么优秀的我还是可以被调用
复制代码
'hello'
复制代码
next(generator) # 好吧玩脱了
复制代码
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-18-8b45440e27eb> in <module>
----> 1 next(generator) # 好吧玩脱了
StopIteration:
复制代码
哦了,生成器就简单介绍到这里, 下面开始正式剖析,这神奇特性的实现原理.
Python运行时核心对象
python世界里,所有东西都是对象,不仅我们看的到基本类型(int, str, list等实例),类本身也是对象哦!但这都不算啥,真正令人叫绝的是,python各种运行时核心组件(代码块, 函数,帧)也都是对象. 下面就依次介绍涉及生成器流程的各个核心对象。(为了使文章不至于太枯燥,将穿插一段狗血虐心的言情剧,大致剧情是女神(一段生成器代码)如何在一个个备胎的助攻下,最终跟渣男(cpu)走在了一起)
PyCodeObject(1号备胎)
当python代码(py文件)被python虚拟机编译后(即将python源码转为python字节码),会将编译结果保存到pyc文件中,pyc文件里 保存的格式就是PyCodeObject的序列化格式.因此他是女神的第一个备胎.PyCodeObject 真容如下:
typedef struct {
PyObject_HEAD
int co_argcount; /* #arguments, except *args */
int co_nlocals; /* #local variables */
int co_stacksize; /* #entries needed for evaluation stack */
int co_flags; /* CO_..., see below */
PyObject *co_code; /* instruction opcodes python字节码,女神本尊*/
PyObject *co_consts; /* list (constants used) */
PyObject *co_names; /* list of strings (names used) */
PyObject *co_varnames; /* tuple of strings (local variable names) */
PyObject *co_freevars; /* tuple of strings (free variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
/* The rest doesn't count for hash/cmp */
PyObject *co_filename; /* string (where it was loaded from) 认识女神的地方*/
PyObject *co_name; /* string (name, for reference) 女神的名字*/
int co_firstlineno; /* first source line number */
PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See
Objects/lnotab_notes.txt for details. */
void *co_zombieframe; /* for optimization only (see frameobject.c) */
PyObject *co_weakreflist; /* to support w