函數(shù),是一個(gè)動(dòng)態(tài)的過程,在函數(shù)被調(diào)用時(shí),系統(tǒng)會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)棧幀,函數(shù)對(duì)應(yīng)的表示結(jié)構(gòu):
typedef struct {
PyObject_HEAD
PyObject *func_code; /* A code object */
PyObject *func_globals; /* 函數(shù)運(yùn)行時(shí)的global名稱空間 */
PyObject *func_defaults; /* 函數(shù)默認(rèn)參數(shù) NULL or a tuple */
PyObject *func_closure; // 用于實(shí)現(xiàn)閉包的
PyObject *func_doc; /* The __doc__ attribute, can be anything */
PyObject *func_name; /* The __name__ attribute, a string object */
PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
PyObject *func_weakreflist; /* List of weak references */
PyObject *func_module; /* The __module__ attribute, can be anything */
} PyFunctionObject;
函數(shù)內(nèi)包含的func_code則對(duì)應(yīng)的是函數(shù)的代碼,靜態(tài)的、在編譯時(shí)就定義好了,而函數(shù)則是在運(yùn)行時(shí),即運(yùn)行到def語句時(shí)構(gòu)建(func_globals是在這時(shí)候才能確定的)
所以,一段代碼肯定對(duì)應(yīng)一個(gè)code object,但是函數(shù)對(duì)象可能有多個(gè),例如多次調(diào)用該函數(shù),會(huì)構(gòu)建多個(gè)func object,這些func object的func_code都指向一個(gè)code object。
函數(shù)對(duì)象的創(chuàng)建
注意,Python在執(zhí)行到def語句后,接著執(zhí)行其后的代碼,而不是進(jìn)入到函數(shù)體內(nèi)執(zhí)行其中的語句(這些語句是在編譯時(shí)就已經(jīng)構(gòu)建好了對(duì)應(yīng)的code object),函數(shù)的聲明和實(shí)現(xiàn)時(shí)分離的,分在了不同的code object中。
def語句
對(duì)應(yīng)的機(jī)器碼有三條:
load_const 0
裝載編譯好的函數(shù)體的code object
make_function
構(gòu)建函數(shù)對(duì)象
store_name 0
把構(gòu)建好的函數(shù)對(duì)象添加到名稱空間內(nèi)
make_function
pop
把之前裝載的函數(shù)體的code object取出來
PyFunction_New(v, f->f_globals)
主要是分配函數(shù)對(duì)象的空間,給函數(shù)對(duì)象中的func_code, func_globals等賦值。
push
把創(chuàng)建好的函數(shù)對(duì)象壓入運(yùn)行時(shí)棧中,最后通過store_name添加。
函數(shù)調(diào)用
f()
對(duì)應(yīng)的機(jī)器碼五條:
load_name 0
獲取該函數(shù)對(duì)象
call_function 0
調(diào)用
pop_top
load_const
return_value
返回值