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

本文为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 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类型

图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

时间: 2024-10-29 00:27:09

《python源码剖析》笔记 Python的编译结果的相关文章

Python源码剖析笔记3-Python执行原理初探

Python源码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源码剖析笔记,然而慢慢觉得没有从一个宏观的角度理解python执行原理的话,从底向上分析未免太容易让人疑惑,不如先从宏观上对python执行原理有了一个基本了解,再慢慢探究细节,这样也许会好很多.这也是最近这么久没有更新了笔记了,一直在看源码剖析书籍和源码,希望能够从一个宏观层面理清python执行原理.人说读书从薄读厚,再从厚读薄方是理解了

Python源码剖析笔记0 ——C语言基础

python源码剖析笔记0--C语言基础回顾 要分析python源码,C语言的基础不能少,特别是指针和结构体等知识.这篇文章先回顾C语言基础,方便后续代码的阅读. 1 关于ELF文件 linux中的C编译得到的目标文件和可执行文件都是ELF格式的,可执行文件中以segment来划分,目标文件中,我们是以section划分.一个segment包含一个或多个section,通过readelf命令可以看到完整的section和segment信息.看一个栗子: char pear[40]; static

Python源码剖析笔记4-内建数据类型

Python源码剖析笔记4-内建数据类型 Python内建数据类型包括整数对象PyIntObject,字符串对象PyStringObject,列表对象PyListObject以及字典对象PyDictObject等.整数对象之前已经分析过了,这一篇文章准备分析下余下几个对象,这次在<python源码剖析>中已经写的很详细的部分就不赘述了,主要是总结一些之前看书时疑惑的地方. 1 整数对象-PyIntObject 参见 python整数对象. 2 字符串对象-PyStringObject 2.1

Python源码剖析笔记2-Python整数对象

Python源码剖析笔记2-Python整数对象 本文简书地址: http://www.jianshu.com/p/0136ed90cd46 千里之行始于足下,从简单的类别开始分析,由浅入深也不至于自己丧失信心.先来看看Python整数对象,也就是python中的PyIntObject对象,对应的类型对象是PyInt_Type. 1 Python整数对象概览 为了性能考虑,python中对小整数有专门的缓存池,这样就不需要每次使用小整数对象时去用malloc分配内存以及free释放内存.pyth

python源码剖析笔记1——Python对象初见

python源码剖析笔记1--Python对象初见 工作整两年了,用python最多,然而对于python内部机制不一定都清楚,每天沉醉于增删改查的简单逻辑编写,实在耗神.很多东西不用就忘记了,比如C语言,正好,python源码用C写的,分析python源码的同时又能温故C语言基础,实在是件很好的事情.另外,还有陈儒大神的<python源码剖析>做指引,分析也不至于没头没脑.期望在一个月的业余时间,能有所小成,以此为记. 1 python中的对象 python中,一切东西都是对象,在c语言实现

Python源码剖析笔记6-函数机制

Python的函数机制是很重要的部分,很多时候用python写脚本,就是几个函数简单解决问题,不需要像java那样必须弄个class什么的. 本文简书地址:http://www.jianshu.com/p/d00108741a18 1 函数对象PyFunctionObject PyFunctionObject对象的定义如下: typedef struct { PyObject_HEAD PyObject *func_code; /* A code object */ PyObject *func

Python源码剖析笔记5-模块机制

本文简书地址: http://www.jianshu.com/p/14586ec50ab6 python中经常用到模块,比如import xxx,from xxx import yyy这样子,里面的机制也是需要好好探究一下的,这次主要从黑盒角度来探测模块机制,源码分析点到为止,详尽的源码分析见陈儒大神的<python源码剖析>第14章. 1 如何导入模块 首先来看一个导入模块的例子.创建一个文件夹demo5,文件夹中有如下几个文件. [email protected] ~/demo5 $ ls

《python源码剖析》笔记 Python虚拟机框架

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1. Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令, 并在当前的上下文环境中执行这条字节码指令. Python虚拟机实际上是在模拟操作中执行文件的过程 PyCodeObject对象中包含了字节码指令以及程序的所有静态信息,但没有包含 程序运行时的动态信息--执行环境(PyFrameObject) 2.Python源码中的PyFrameObject

《python源码剖析》笔记 python虚拟机中的函数机制

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.Python虚拟机在执行函数调用时会动态地创建新的 PyFrameObject对象, 这些PyFrameObject对象之间会形成PyFrameObject对象链,模拟x86平台上运行时栈 2.PyFuctionObject对象 typedef struct { PyObject_HEAD PyObject *func_code: //对应函数编译后的PyCodeObject对象 Py