您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Python編譯結(jié)果之如何理解code對(duì)象與pyc文件”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
與java類似,Python將.py編譯為字節(jié)碼,然后通過虛擬機(jī)執(zhí)行。編譯過程與虛擬機(jī)執(zhí)行過程均在python25.dll中。Python虛擬機(jī)比java更抽象,離底層更遠(yuǎn)。
編譯過程不僅生成字節(jié)碼,還要包含常量、變量、占用棧的空間等,Pyton中編譯過程生成code對(duì)象PyCodeObject。將PyCodeObject寫入二進(jìn)制文件,即.pyc。
有必要?jiǎng)t寫入A.pyc指的是該.py是否只運(yùn)行一次,如果import的模塊,肯定會(huì)生成.pyc。
Python解釋器將.py程序編譯為PyCodeObject對(duì)象,具體過程與編譯原理類似。
typedef struct { PyObject_HEAD int co_argcount; // Code Block的參數(shù)的個(gè)數(shù),比如說一個(gè)函數(shù)的參數(shù) int co_nlocals; // Code Block中局部變量的個(gè)數(shù) int co_stacksize; // 執(zhí)行該段Code Block需要的??臻g int co_flags; // N/A PyObject *co_code; // Code Block編譯所得的byte code,以PyStringObject的形式存在 PyObject *co_consts; // PyTupleObject對(duì)象,保存Code Block中的常量 PyObject *co_names; // PyTupleObject對(duì)象,保存Code Block中的所有符號(hào) PyObject *co_varnames; // Code Block中局部變量名集合 PyObject *co_freevars; // 實(shí)現(xiàn)閉包所需東西 PyObject *co_cellvars; // Code Block內(nèi)部嵌套函數(shù)所引用的局部變量名集合 PyObject *co_filename; // Code Block所對(duì)應(yīng)的.py文件的完整路徑 PyObject *co_name; // Code Block的名字,通常是函數(shù)名或類名 int co_firstlineno; // Code Block在對(duì)應(yīng)的.py文件中的起始行 PyObject *co_lnotab; // byte code與.py文件中source code行號(hào)的對(duì)應(yīng)關(guān)系,以PyStringObject的形式存在 void *co_zombieframe; PyObject *co_weakreflist; } PyCodeObject;
一個(gè)Code Block生成一個(gè)PyCodeObject,進(jìn)入一個(gè)名字空間成為進(jìn)入一個(gè)Code Block。如下.py文件編譯完成后會(huì)生成三個(gè)PyCodeObject,一個(gè)對(duì)應(yīng)整個(gè).py文件一個(gè)對(duì)應(yīng)Class A,一個(gè)對(duì)應(yīng)def Fun。實(shí)際這三個(gè)code對(duì)象是嵌套的,后兩個(gè)code對(duì)象位于第一個(gè)code對(duì)象的co_consts屬性中。其實(shí),字節(jié)碼位于co_code中。
class A: pass def Fun(): pass a = A() Fun()
pyc文件包括三部分:
(1)四字節(jié)的Magic int,表示pyc版本信息
(2)四字節(jié)的int,是pyc產(chǎn)生時(shí)間,若與py文件時(shí)間不同會(huì)重新生成
(3)序列化了的PyCodeObject對(duì)象。
寫入pyc文件的函數(shù)包括以下幾個(gè)步驟:
PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION); // 寫入版本信息 PyMarshal_WriteLongToFile(0L, fp, Py_MARSHAL_VERSION); // 寫入時(shí)間信息 PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION); // 寫入PyCodeObject對(duì)象
關(guān)鍵在于code對(duì)象的寫入:
{ WFILE wf; wf.fp = fp; …… w_object(x, &wf); }
用到了一個(gè)WFILE結(jié)構(gòu)體,可以認(rèn)為是對(duì)FILE *fp 的一個(gè)封裝:
typedef struct { FILE *fp; int error; int depth; PyObject *strings; // 存儲(chǔ)字符串,寫入時(shí)以dict形式,讀出時(shí)以list形式 } WFILE;
關(guān)鍵在于w_object()函數(shù):
static void w_object(PyObject *v, WFILE *p){ if (v == NULL) …… else if (PyInt_CheckExact(v)) …… else if (PyFloat_CheckExact(v)) …… else if (PyString_CheckExact(v)) …… else if (PyList_CheckExact(v)) …… }
w_code實(shí)質(zhì)為根據(jù)不同的對(duì)象類型選取不同的策略,例如tuple對(duì)象:
else if (PyTuple_CheckExact(v)) { w_byte(TYPE_TUPLE, p); n = PyTuple_Size(v); W_SIZE(n, p); for (i = 0; i < n; i++) w_object(PyTuple_GET_ITEM(v, i), p);
而所有類型最終可分解為寫入數(shù)值與寫入字符串兩種操作,涉及以下幾部分:
#define w_byte(c, p) putc((c), (p)->fp) // 用于寫入類型 static void w_long(long x, WFILE *p){ // 用于寫入數(shù)字 w_byte((char)( x & 0xff), p); // 實(shí)質(zhì)為用四個(gè)字節(jié)存儲(chǔ)一個(gè)數(shù)字 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(char *s, int n, WFILE *p){ //用于寫入字符串 fwrite(s, 1, n, p->fp); }
由于序列化寫入文件后丟失了結(jié)構(gòu)信息,故寫入每個(gè)對(duì)象時(shí)寫入類型信息w_byte:
#define TYPE_INT 'i' #define TYPE_LIST '[' #define TYPE_DICT '{' #define TYPE_CODE 'c'
由于Python皆對(duì)象,w_object(PyObject*)便可針對(duì)不同類型選取不同寫入方法,不斷細(xì)分,最終分解為PyInt_Object或PyString_Object,利用w_long或w_string寫入。
數(shù)字比較簡(jiǎn)單:
else if (PyInt_CheckExact(v)) { w_byte(TYPE_INT, p); w_long(x, p); }
字符串則比較復(fù)雜:
else if (PyString_CheckExact(v)) { if (p->strings && PyString_CHECK_INTERNED(v)) { PyObject *o = PyDict_GetItem(p->strings, v); // 獲取在strings中的序號(hào) if (o) { // inter對(duì)象的非首次寫入 long w = PyInt_AsLong(o); w_byte(TYPE_STRINGREF, p); w_long(w, p); goto exit; } else { // intern對(duì)象的首次寫入 int ok; ok = o && PyDict_SetItem(p->strings, v, o) >= 0; Py_XDECREF(o); w_byte(TYPE_INTERNED, p); } } else { // 寫入普通string w_byte(TYPE_STRING, p); } n = PyString_GET_SIZE(v); W_SIZE(n, p); w_string(PyString_AS_STRING(v), n, p); }
(1)若寫入普通字符串,寫入字符串類型信息"S",然后寫入字符串長(zhǎng)度及string值。
(2)若寫入inter字符串,先到WFILE的strings中查找:
(a)若找到,則寫入引用類型信息"R",然后寫入序號(hào)
(b)若未找到,創(chuàng)建對(duì)象放入strings,并寫入intern類型信息"t",然后寫入字符串長(zhǎng)度及string值。
若依次寫入"efei"、"snow"、"efei",則會(huì)如下:
從pyc文件讀入時(shí),依靠list,那么序號(hào)就可以利用上了。
“Python編譯結(jié)果之如何理解code對(duì)象與pyc文件”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。