typedef struct _frame {
PyObject_VAR_HEAD
struct _frame *f_back; /* previous frame, or NULL */
PyCodeObject *f_code; /* code segment */
PyObject *f_builtins; /* builtin symbol table (PyDictObject) */
PyObject *f_globals; /* global symbol table (PyDictObject) */
PyObject *f_locals; /* local symbol table (any mapping) */
PyObject **f_valuestack; /* points after the last local */
/* Next free slot in f_valuestack. Frame creation sets to f_valuestack.
Frame evaluation usually NULLs it, but a frame that yields sets it
to the current stack top. */
PyObject **f_stacktop;
PyObject *f_trace; /* Trace function */
/* If an exception is raised in this frame, the next three are used to
* record the exception info (if any) originally in the thread state. See
* comments before set_exc_info() -- it's not obvious.
* Invariant: if _type is NULL, then so are _value and _traceback.
* Desired invariant: all three are NULL, or all three are non-NULL. That
* one isn't currently true, but "should be".
*/
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
PyThreadState *f_tstate;
int f_lasti; /* Last instruction if called */
/* Call PyFrame_GetLineNumber() instead of reading this field
directly. As of 2.3 f_lineno is only valid when tracing is
active (i.e. when f_trace is set). At other times we use
PyCode_Addr2Line to calculate the line from the current
bytecode index. */
int f_lineno; /* Current line number */
int f_iblock; /* index in f_blockstack */
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
} PyFrameObject;
这里我们重点关注下以下的成员:
PyObject **f_stacktop;
...
PyCodeObject *f_code; /* code segment */
PyObject *f_globals; /* global symbol table (PyDictObject) */
PyObject *f_locals; /* local symbol table (any mapping) */
0
0
3
0040
6400006401006c00005a00006501005a02006402008400005a0300640300
640500640400840000830000595a04006504008300005a05006505006a06
008300000164010053
...
('dis', 'True', 'myglobal', 'add', 'world', 'w', 'sayHello')
()
()
()
'test.py'
''
1
-1
None
...
1 0 LOAD_CONST 0 (-1) #加载consts数组中索引为0处的值,这里为数值-1
3 LOAD_CONST 1 (None) #加载consts数组中索引为1处的值,这里为None
6 IMPORT_NAME 0 (dis) #加载dis模块:names[0]即为"dis"
9 STORE_NAME 0 (dis) #将模块保存到一个dict中,这个dict专门用来保存局部变量的值,key为names[0],即"dis"
2 12 LOAD_NAME 1 (True) #将names[1],即True压栈。
15 STORE_NAME 2 (myglobal) #将栈顶的元素,即True保存到locals['myglobal']中,names[2]即为字符串'myglobal'
4 18 LOAD_CONST 2 () #将consts[2],即add函数的PyCodeObject压栈。
21 MAKE_FUNCTION 0 #通过add函数的PyCodeObject创建一个函数,并压入栈顶。
24 STORE_NAME 3 (add) #将创建的函数出栈并保存到locals['add']中
9 27 LOAD_CONST 3 ('world') #consts[3],即"world"入栈
30 LOAD_CONST 5 (()) #consts[5],即空数组入栈
33 LOAD_CONST 4 () #将consts[4],即world的PyCodeObject入栈。
36 MAKE_FUNCTION 0 #创建函数
39 CALL_FUNCTION 0 #调用刚创建的函数,用于初始化类,会返回一个dict到栈顶,这个dict中保存了类的方法以及一些全局变量,
#具体的实现要看world类的PyCodeObject中的opcode
42 BUILD_CLASS #创建类,注意BUILD_CLASS会用到栈中的三个对象,这里分别为:保存在dict中的类的信息,基类数组,这里为空数组(),类的名称:"world"
43 STORE_NAME 4 (world) #将类保存到locals['world']中
15 46 LOAD_NAME 4 (world)
49 CALL_FUNCTION 0
52 STORE_NAME 5 (w)
#以上三行代码创建一个world对象
16 55 LOAD_NAME 5 (w)
58 LOAD_ATTR 6 (sayHello)
61 CALL_FUNCTION 0
#以上三行代码调用w.sayHello()
64 POP_TOP
65 LOAD_CONST 1 (None)
68 RETURN_VALUE