822
京東網上商城
[筆記]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