標籤:
本文和大家分享的主要是python中的pyc檔案與code對象相關內容,一起來看看吧,希望對大家學習python有所協助。 python對來源程式編譯結果是產生一個 .pyc 檔案. python對 .py 檔案的編譯結果是位元組碼, 為了能複用而不需要重新編譯才有了寫成 .pyc 檔案. 對於解譯器來說 PyCodeObject 對象才是真正編譯結果, pyc檔案只是這個對象在硬碟上的表現形式. PyCodeObject [code.h]typedef struct { PyObject_HEAD int co_argcount; /* #arguments, except *args */ int co_kwonlyargcount; /* #keyword only arguments */ int co_nlocals; /* #local variables */ int co_stacksize; /* #entries needed for evaluation stack */ int co_flags; /* CO_..., see below */ int co_firstlineno; /* first source line number */ PyObject *co_code; /* instruction opcodes */ PyObject *co_consts; /* list (constants used) */ PyObject *co_names; /* list of strings (names used) */ PyObject *co_varnames; /* tuple of strings (local variable names) */ PyObject *co_freevars; /* tuple of strings (free variable names) */ PyObject *co_cellvars; /* tuple of strings (cell variable names) */ void *co_extra; } PyCodeObject; 編譯器在對原始碼進行編譯的時候, 每一個 Code Block 會建立一個 PyCodeObject 對象與這個程式碼片段相對應. 程式碼片段的範圍可大可小. 可以是整個py檔案, 可以是class, 可以是函數. 訪問PyCodeObject對象 在python中, 有與c一級的對PyCodeObject簡單封裝, code對象, 可以訪問到PyCodeObject的各個域. >>> source = open(’db.py’).read()>>> co = compile(source, ’db.py’, ’exec’)>>> type(co) <class ’code’>>>> co.co_names (’pymysql’, ’config’, ’threading’, ’RLock’, ’Lock’, ’create_table_template’, ’ob ject’, ’Model’, ’str’, ’m’) 寫入檔案 PyMarshal_WriteObjectToFile 向pyc檔案寫入資料主要是這幾個, 有刪減: [marshal.c] typedef struct { FILE *fp; int depth; PyObject *str; char *ptr; char *end; char *buf; _Py_hashtable_t *hashtable; int version; } WFILE; #define w_byte(c, p) do { if ((p)->ptr != (p)->end || w_reserve((p), 1)) *(p)->ptr++ = (c); } while(0) static void w_flush(WFILE *p) { assert(p->fp != NULL); fwrite(p->buf, 1, p->ptr - p->buf, p->fp); p->ptr = p->buf; } 這是檔案寫入定義的基本結構, fp指向最後要寫入的檔案, w_byte(c, p) 則是一個簡單的封裝, 以位元組為單位的複製到p->ptr先行區中. w_flush(WFILE *p) 則是將緩衝區 p->buf 寫到檔案中.p->ptr也就是準備寫到檔案中的增量部分. static void w_long(long x, WFILE *p) { w_byte((char)( x & 0xff), p); w_byte((char)((x>> 8) & 0xff), p); w_byte((char)((x>>16) & 0xff), p); w_byte((char)((x>>24) & 0xff), p); } static void w_string(const char *s, Py_ssize_t n, WFILE *p) { Py_ssize_t m; if (!n || p->ptr == NULL) return; m = p->end - p->ptr; if (p->fp != NULL) { if (n <= m) { memcpy(p->ptr, s, n); p->ptr += n; } else { w_flush(p); fwrite(s, 1, n, p->fp); } } else { if (n <= m || w_reserve(p, n - m)) { memcpy(p->ptr, s, n); p->ptr += n; } } } 如在調用 PyMarshal_WriteLongToFile 時, 會調用 w_long , 資料將會一個位元組位元組的寫入到檔案中. 而調用PyMarshal_WriteObjectToFile 也會調用 w_object , 這個函數比較長,就不列出來了. 為了區分寫入的類型, 在寫入檔案前會做一個動作,就是先將待寫入的物件類型寫進去: [marshal.c] #define TYPE_NULL ’0’ #define TYPE_NONE ’N’ #define TYPE_FALSE ’F’ #define TYPE_TRUE ’T’ #define TYPE_STOPITER ’S’ #define W_TYPE(t, p) do { w_byte((t) | flag, (p)); } while(0) 這個物件類型的標識對讀取pyc檔案至關重要, 因為對象寫入pyc檔案後, 所有資料都變成位元組流, 類型資訊丟失. 有了這個標識, 當讀取這樣的標識時, 則預示著上一個對象結束, 新的對象開始, 也能知道新對象是什麼類型的. 內部也有機制處理共用對象, 減少位元組碼中冗餘資訊. 共用類型的屬於 TYPE_INTERNED . 載入pyc檔案 PyMarshal_ReadObjectFromFile 看一下載入pyc檔案的過程, 讓pyc檔案理解更加深刻: PyObject * PyMarshal_ReadObjectFromFile(FILE *fp) { RFILE rf; PyObject *result; rf.fp = fp; rf.readable = NULL; rf.current_filename = NULL; rf.depth = 0; rf.ptr = rf.end = NULL; rf.buf = NULL; rf.refs = PyList_New(0); if (rf.refs == NULL) return NULL; result = r_object(&rf); Py_DECREF(rf.refs); if (rf.buf != NULL) PyMem_FREE(rf.buf); return result; } 從 r_object 開始就開始從pyc檔案中讀入資料, 並建立PyCodeObject對象, 這個 r_object 是對 w_object 的逆運算. 當讀到 TYPE_INTERNED 後, 會將其後面的字串讀入, 將這個字串進行intern操作.來源:棲遲於一丘
Python學習之pyc檔案與code對象