《python源码剖析》笔记 Python的编译结果,,本文为senlie原


本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie


1.python的执行过程
1)对python源代码进行编译,产生字节码
2)将编译结果交给python虚拟机,由虚拟机按照顺序一条一条地执行字节码,产生执行结果
图7-1


2.Python编译器的编译结果——PyCodeObject对象
Python编译器的编译结果中包含了字符串、常量值、字节码等在源代码中出现的一切有用的静态信息。
在Python运行期间,这些静态信息被PyCodeObject对象中
在Python运行结束后,这些信息会被存储在pyc文件中
PyCodeObject对象和pyc文件是Python对源文件编译结果的两种不同存在形式


3.Python源码中的PyCodeObject

/* Bytecode object */typedef struct {    PyObject_HEAD    int co_argcount;/* 位置参数个数*/    int co_nlocals;/* 局部变量个数,包括位置参数个数*/    int co_stacksize;/* 需要的栈空间 */    int co_flags;/* CO_..., see below */    PyObject *co_code;/* 字节码指令序列,以PyStringObject形式存在 */    PyObject *co_consts;/* PyTupleObject对象,保存所有的常量 */    PyObject *co_names;/* PyTupleObject对象,保存所有符号 */    PyObject *co_varnames;/* 局部变量名集合 */    PyObject *co_freevars;/* 实现闭包需要用到的东西 */    PyObject *co_cellvars;      /* 内部嵌套函数所引用的局部变量名集合 */    /* The rest doesn't count for hash/cmp */    PyObject *co_filename;/* Code Block所对应的.py文件的完整路径 */    PyObject *co_name;/* Code Block的名字,通常是函数名或类名 */    int co_firstlineno;/* Code Block所对应的.py文件的起始行 */    PyObject *co_lnotab;/* 字节码指令与.py文件中source code行号的对应关系,以PyStringObject的等式存在 */    void *co_zombieframe;     /* for optimization only (see frameobject.c) */    PyObject *co_weakreflist;   /* to support weakrefs to code objects */} PyCodeObject;

Code Block:当进入一个新的名字空间,或者说作用域的时候,就算是进入了一个新的Code Block:当进入一个新的名字空间,或者说作用域的时候,就算是进入了一个新的
Code Block。一个 Code Block对应一个 PyCodeObject
名字空间是符号的上下文环境。名字空间链是多个名字空间嵌套在一起。
在Python中,module、类、函数都对应着一个独立的名字空间

#会产生三个 PyCodeObject,分别对应整个文件,class A和 def Funclass A:passdef Fun():passa = A()Fun()

3.pyc文件
pyc文件中包含了三部分独立的信息:
Python的magic number --> 保证Python的兼容性
pyc文件的创建时间 --> 可以使Python自动将pyc文件与最新的py文件进行同步
PyCodeObject对象


将内存中的PyCodeObject对象写入到pyc文件,需要以下几个函数
w_byte
w_long
w_string


PyMarshal_WriteObjectToFile会调用w_object,w_object会遍历 PyCodeObject中的所有域,
将这些域依次写入。
写入最终归结为两种形式:对数值的写入和对字符串的写入


static void w_object(PyObject *v, WFILE *p){//……else if (PyCode_Check(v)){PyCodeObject *co = (PyCodeObject *)v;w_byte(TYPE_CODE, p); w_long(co->co_argcount, p);w_long(co->co_nlocals, p);w_long(co->co_stacksize, p);w_long(co->co_flags, p);w_object(co->co_code, p);w_object(co->co_consts, p);w_object(co->co_names, p);w_object(co->co_varnames, p);w_object(co->co_freevars, p);w_object(co->co_cellvars, p);w_object(co->co_filename, p);w_object(co->co_name, p);w_long(co->co_firstlineno, p);w_object(co->co_lnotab, p);}//…… }

w_object在写入对象之前会先写入TYPE_LIST、TYPE_CODE或者TYPE_INT标识,它们对
pyc文件的再次加载具有至关重要的作用。Python在pyc文件中发现这样的标识,则预示着
一个对象的结束,新对象的开始,而且也知道了对象的类型。


4.向pyc文件中写入字符串
写入过程中的结构体WFILE
typedef struct {FILE *fp;PyObject *strings; //在写入时指向dict,在读出时指向list} WFILE;

WFILE中的strings在marshal的时候指向一个PyDictObject对象(PyStringObject,PyIntObject)
//w_object对于字符串的处理else if (PyString_CheckExact(v)) {        if (p->strings && PyString_CHECK_INTERNED(v)) {//[1]:获得 PyStringObject对象在strings中的序号            PyObject *o = PyDict_GetItem(p->strings, v);//[2]:intern字符串的非首次写入            if (o) {                long w = PyInt_AsLong(o);                w_byte(TYPE_STRINGREF, p);                w_long(w, p);                goto exit;            }//[3]:intern字符串的首次写入            else {                int ok;                o = PyInt_FromSsize_t(PyDict_Size(p->strings));                ok = o &&                     PyDict_SetItem(p->strings, v, o) >= 0;                Py_XDECREF(o);                if (!ok) {                    p->depth--;                    p->error = WFERR_UNMARSHALLABLE;                    return;                }                w_byte(TYPE_INTERNED, p);            }        }//[4]:写入普通字符串        else {            w_byte(TYPE_STRING, p);        }//写入字符串        w_pstring(PyBytes_AS_STRING(v), PyString_GET_SIZE(v), p);}//...

怎么确定一个字符串要不要intern?
写入pyc文件时,strings是dict类型
图7-6
从pyc文件中读取的时候,strings是list类型
图7-7


5.一个PyCodeObject,多个PyCodeObject
PyCodeObject中的co_consts是嵌套的PyCodeObject的藏身之处
图7-8

6.Python的字节码
104条字节码
STOP_CODE() Indicates end-of-code to the compiler, not used by the interpreter.NOP() Do nothing code. Used as a placeholder by the bytecode optimizer.POP_TOP() Removes the top-of-stack (TOS) item.ROT_TWO() Swaps the two top-most stack items.ROT_THREE() Lifts second and third stack item one position up, moves top down to position three.ROT_FOUR() Lifts second, third and forth stack item one position up, moves top down to position four.//...


《python源码剖析》笔记 Python的编译结果,布布扣,bubuko.com

《python源码剖析》笔记 Python的编译结果

评论关闭