标签:
	本文和大家分享的主要是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 
	 memcpy(p->ptr, s, n);
	 p->ptr += n;
	 }
	 else {
	 w_flush(p);
	 fwrite(s, 1, n, p->fp);
	 }
	 }
	 else {
	 if (n 
	 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对象
标签: