[筆記]Python虛擬機對函數的解釋
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
最後更新:2017-04-02 22:16:35