本文和大家分享的主要是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 操作 .

栖迟於一丘