python解釋器在執行python腳本文件時,對文件中的python源代碼進行編譯,編譯的結果就是byte code(字節碼)python
python虛擬機執行編譯好的字節碼,完成程序的運行閉包
python會爲導入的模塊建立字節碼文件python2.7
當a.py依賴b.py時,如在a.py中import b函數
python先檢查是否有b.pyc文件(字節碼文件),若是有,而且修改時間比b.py晚,就直接調用b.pyc源碼分析
不然編譯b.py生成b.pyc,而後加載新生成的字節碼文件spa
每一個py文件會包含許多代碼塊(Code Block)rest
每一個代碼塊(Code Block)會編譯建立一個字節碼對象(PyCodeObject)code
PyCodeObject 對象自己是嵌套的,根據代碼塊的結構嵌套對象
子 PyCodeObject 對象保存在父對象的 co_consts 變量中blog
# 整個文件是一個代碼塊 1 # 代碼塊 2 class TestA(object): pass # 代碼塊 3 class TestB(object): pass # 代碼塊 4 def show(): print 'show ...' a = TestA()show()
/* 字節碼對象 */ typedef struct { PyObject_HEAD int co_argcount; /* Code Block 參數個數 */ int co_nlocals; /* 局部變量個數 */ int co_stacksize; /* 棧空間 */ int co_flags; /* CO_..., see below */ PyObject *co_code; /* 字節碼指令序列 */ PyObject *co_consts; /* 常量列表(子代碼塊也在這裏) */ PyObject *co_names; /* 字符串符號列表 */ PyObject *co_varnames; /* 局部變量名 */ PyObject *co_freevars; /* 閉包所須要的變量 */ PyObject *co_cellvars; /* 嵌套函數所引用的局部變量名 */ /* The rest doesn't count for hash/cmp */ PyObject *co_filename; /* py文件路徑 */ PyObject *co_name; /* Code block 的函數名或類名 */ int co_firstlineno; /* Code block 起始行 */ PyObject *co_lnotab; /* 字節碼指令與源代碼的對應關係 */ void *co_zombieframe; /* for optimization only (see frameobject.c) */ PyObject *co_weakreflist; /* to support weakrefs to code objects */ } PyCodeObject;
字節碼對象序列化到硬盤的結果,就是字節碼文件。這個過程是一個遞歸寫入的過程。
static void w_object(PyObject *v, WFILE *p){ Py_ssize_t i, n; p->depth++; /* ... */ else if (PyCode_Check(v)) { PyCodeObject *co = (PyCodeObject *)v; w_byte(TYPE_CODE, p); w_long(co->co_argcount, p); w_long(co->co_nlocals, p); w_long(co->co_stacksize, p); w_long(co->co_flags, p); w_object(co->co_code, p); w_object(co->co_consts, p); w_object(co->co_names, p); w_object(co->co_varnames, p); w_object(co->co_freevars, p); w_object(co->co_cellvars, p); w_object(co->co_filename, p); w_object(co->co_name, p); w_long(co->co_firstlineno, p); w_object(co->co_lnotab, p); } /* ... */ }
字節碼文件在反序列化時建立字節碼對象的結構,虛擬機在執行的時候根據字節碼對象執行程序功能。