zoukankan      html  css  js  c++  java
  • 《python源代码剖析》笔记 Python的编译结果

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


    1.python的运行过程
    1)对python源码进行编译。产生字节码
    2)将编译结果交给python虚拟机。由虚拟机依照顺序一条一条地运行字节码,产生运行结果



    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 Fun
    class A:
    	pass
    def Fun():
    	pass
    a = 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类型

    从pyc文件里读取的时候,strings是list类型



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


    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.
    //...


  • 相关阅读:
    scnner02 (nextLine)
    Scanner01
    Spring 框架 (初学)
    查询自己写了多少行代码
    jdbc事务
    jdbc(预编译插入数据)
    jdbc(java连接数据库)
    监听器扩展
    listener(监听器)
    Filter过滤器
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7206157.html
Copyright © 2011-2022 走看看