本文和大家分享的主要是python
中的pyc
文件與
code
對象相關(guān)內(nèi)容,一起來看看吧,希望對大家
學(xué)習(xí)python有所幫助。
python
對源程序編譯結(jié)果是生成一個
.pyc
文件. python
對
.py
文件的編譯結(jié)果是字節(jié)碼,
為了能復(fù)用而不需要重新編譯才有了寫成
.pyc
文件.
對于解釋器來說
PyCodeObject
對象才是真正編譯結(jié)果, pyc
文件只是這個對象在硬盤上的表現(xiàn)形式
.
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;
編譯器在對源代碼進(jìn)行編譯的時候,
每一個
Code Block
會創(chuàng)建一個
PyCodeObject
對象與這個代碼段相對應(yīng).
代碼段的范圍可大可小
.
可以是整個
py
文件
,
可以是
class,
可以是函數(shù)
.
訪問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
文件寫入數(shù)據(jù)主要是這幾個
,
有刪減
:
[
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;
}
這是文件寫入定義的基本結(jié)構(gòu), fp
指向最后要寫入的文件
,
w_byte(c, p)
則是一個簡單的封裝,
以字節(jié)為單位的復(fù)制到
p->ptr
先行區(qū)中
.
w_flush(WFILE *p)
則是將緩沖區(qū)
p->buf
寫到文件中.p->ptr
也就是準(zhǔn)備寫到文件中的增量部分
.
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;
}
}
}
如在調(diào)用
PyMarshal_WriteLongToFile
時,
會調(diào)用
w_long ,
數(shù)據(jù)將會一個字節(jié)字節(jié)的寫入到文件中
.
而調(diào)用
PyMarshal_WriteObjectToFile
也會調(diào)用
w_object ,
這個函數(shù)比較長
,
就不列出來了
.
為了區(qū)分寫入的類型,
在寫入文件前會做一個動作
,
就是先將待寫入的對象類型寫進(jìn)去
:
[
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)
這個對象類型的標(biāo)識對讀取pyc
文件至關(guān)重要
,
因為對象寫入
pyc
文件后
,
所有數(shù)據(jù)都變成字節(jié)流
,
類型信息丟失
.
有了這個標(biāo)識
,
當(dāng)讀取這樣的標(biāo)識時
,
則預(yù)示著上一個對象結(jié)束
,
新的對象開始
,
也能知道新對象是什么類型的
.
內(nèi)部也有機(jī)制處理共享對象,
減少字節(jié)碼中冗余信息
.
共享類型的屬于
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
文件中讀入數(shù)據(jù)
,
并創(chuàng)建
PyCodeObject
對象
,
這個
r_object
是對
w_object
的逆運算.
當(dāng)讀到
TYPE_INTERNED
后,
會將其后面的字符串讀入
,
將這個字符串進(jìn)行
intern
操作
.
來源:
棲遲於一丘