本文和大家分享的主要是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')
写入文件
向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 操作 .
栖迟於一丘