demo.py:
i = 1 def f(): j = 2 f()
test.py:
import dis source = open('./demo.py').read() co = compile(source, './demo.py', 'exec') print(co.co_name) dis.dis(co) print("\n") fCode = co.co_consts[1] print(fCode.co_name) dis.dis(fCode)
输出:
<module> 1 0 LOAD_CONST 0 (1) 3 STORE_NAME 0 (i) 3 6 LOAD_CONST 1 (<code object f at 0x022F5430, file "./demo.py", line 3>) 9 MAKE_FUNCTION 0 12 STORE_NAME 1 (f) 6 15 LOAD_NAME 1 (f) 18 CALL_FUNCTION 0 21 POP_TOP 22 LOAD_CONST 2 (None) 25 RETURN_VALUE f 4 0 LOAD_CONST 1 (2) 3 STORE_FAST 0 (j) 6 LOAD_CONST 0 (None) 9 RETURN_VALUE
对于def f():这一语句,Python虚拟机先将函数f对应的PyCodeObject入栈,然后再MAKE_FUNCTION。
case MAKE_FUNCTION: v = POP(); /* code object */ x = PyFunction_New(v, f->f_globals); Py_DECREF(v); /* XXX Maybe this should be a separate opcode? */ if (x != NULL && oparg > 0) { v = PyTuple_New(oparg); if (v == NULL) { Py_DECREF(x); x = NULL; break; } while (--oparg >= 0) { w = POP(); PyTuple_SET_ITEM(v, oparg, w); } err = PyFunction_SetDefaults(x, v); Py_DECREF(v); } PUSH(x); break;
在相应代码中,将对应的PyCodeObject和帧的全局符号表指针传递给PyFunction_New函数,调用之,最后将返回值入栈。
在PyFunction_New函数中,创建一个PyFunctionObject,然后初始化相应的成员信息,返回之。
而对于函数f的调用,首先将函数名入栈,再执行CALL_FUNCTION:
case CALL_FUNCTION: { PyObject **sp; PCALL(PCALL_ALL); sp = stack_pointer; #ifdef WITH_TSC x = call_function(&sp, oparg, &intr0, &intr1); #else x = call_function(&sp, oparg); #endif stack_pointer = sp; PUSH(x); if (x != NULL) continue; break; }
在这里,将栈顶指针和参数信息传递给函数call_function,然后恢复栈顶指针,将返回值入栈。
最后实际上是创建了一页帧,然后以帧作为活动环境,调用PyEval_EvalFrameEx函数。
f = PyFrame_New(tstate, co, globals, NULL); if (f == NULL) return NULL; fastlocals = f->f_localsplus; stack = (*pp_stack) - n; for (i = 0; i < n; i++) { Py_INCREF(*stack); fastlocals[i] = *stack++; } retval = PyEval_EvalFrameEx(f,0);
JasonLee 2011.08.27 19:52