1.1python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后解释器会从编译得到的PyCodeObject对象中一条一条执行字节码指令,
并在当前的上下文环境中执行这条字节码指令,从而完成程序的执行。Python解释器实际上是在模拟操作中执行文件的过程。PyCodeObject对象
中包含了字节码指令以及程序的所有静态信息,但没有包含程序运行时的动态信息——执行环境(PyFrameObject)
2. 字节码
字节码在python解释器程序里对应的是PyCodeObject对象
.pyc文件是字节码在磁盘上的表现形式
2.1从整体上看:OS中执行程序离不开两个概念:进程和线程。python中模拟了这两个概念,模拟进程和线程的分别是PyInterpreterState和
PyTreadState。即:每个PyThreadState都对应着一个帧栈,python解释器在多个线程上切换。当python解释器开始执行时,它会先进行一
些初始化操作,最后进入PyEval_EvalFramEx函数,它的作用是不断读取编译好的字节码,并一条一条执行,类似CPU执行指令的过程。函数内部
主要是一个switch结构,根据字节码的不同执行不同的代码。
3. .pyc文件
PyCodeObject对象的创建时机是模块加载的时候,及import
Python test.py会对test.py进行编译成字节码并解释执行,但是不会生成test.pyc
如果test.py加载了其他模块,如import urlib2, Python会对urlib2.py进行编译成字节码,生成urlib2.pyc,然后对字节码进行解释
如果想生成test.pyc,我们可以使用Python内置模块py_compile来编译。
加载模块时,如果同时存在.py和pyc,Python会尝试使用.pyc,如果.pyc的编译时间早于.py的修改时间,则重新编译.py并更新.pyc。
4. PyCodeObject
Python代码的编译结果就是PyCodeObject对象
typedef struct {
PyObject_HEAD
int co_argcount/* 位置参数个数 */
int co_nlocals/* 局部变量个数 */
int co_stacksize/* 栈大小 */
int co_flags
PyObject *co_code/* 字节码指令序列 */
PyObject *co_consts/* 所有常量集合 */
PyObject *co_names/* 所有符号名称集合 */
PyObject *co_varnames/* 局部变量名称集合 */
PyObject *co_freevars/* 闭包用的的变量名集合 */
PyObject *co_cellvars/* 内部嵌套函数引用的变量名集合 */
/* The rest doesn’t count for hash/cmp */
PyObject *co_filename/* 代码所在文件名 */
PyObject *co_name/* 模块名|函数名|类名 */
int co_firstlineno/* 代码块在文件中的起始行号 */
PyObject *co_lnotab/* 字节码指令和行号的对应关系 */
void *co_zombieframe/* for optimization only (see frameobject.c) */
} PyCodeObject
5. .pyc文件格式
加载模块时,模块对应的PyCodeObject对象被写入.pyc文件
6.分析字节码
6.1解析PyCodeObject
Python提供了内置函数compile可以编译python代码和查看PyCodeObject对象
6.2指令序列co_code的格式
opcode oparg opcode opcode oparg …
1 byte 2 bytes 1 byte 1 byte 2 bytes
Python内置的dis模块可以解析co_code
7. 执行字节码
Python解释器的原理就是模拟可执行程序再X86机器上的运行,X86的运行时栈帧如下图
Python解释器的原理就是模拟上述行为。当发生函数调用时,创建新的栈帧,对应Python的实现就是PyFrameObject对象。
PyFrameObject对象创建程序运行时的动态信息,即执行环境
7.1 PyFrameObject
typedef struct _frame{
PyObject_VAR_HEAD //"运行时栈"的大小是不确定的
struct _frame *f_back//执行环境链上的前一个frame,很多个PyFrameObject连接起来形成执行环境链表
PyCodeObject *f_code//PyCodeObject 对象,这个frame就是这个PyCodeObject对象的上下文环境
PyObject *f_builtins//builtin名字空间
PyObject *f_globals//global名字空间
PyObject *f_locals//local名字空间
PyObject **f_valuestack//"运行时栈"的栈底位置
PyObject **f_stacktop//"运行时栈"的栈顶位置
//...
int f_lasti//上一条字节码指令在f_code中的偏移位置
int f_lineno//当前字节码对应的源代码行
//...
//动态内存,维护(局部变量+cell对象集合+free对象集合+运行时栈)所需要的空间
PyObject *f_localsplus[1]
} PyFrameObject
每一个 PyFrameObject对象都维护了一个 PyCodeObject对象,这表明每一个 PyFrameObject中的动态内存空间对象都和源代码中的一段Code相对应。
都可以,没有声明其他编码方法,即使用ascii编码作为标准编码方法。
要定义源文件的编码方式,应在文件的第一行或第二行中放置声明,例如:
[python] view plain copy
#coding=<encoding name>
或者(使用流行编辑器中的格式化方式)
[python] view plain copy
#!/usr/bin/python
# -*- coding: <encoding name>-*-
或者
[python] view plain copy
#!/usr/bin/python
# vim: set fileencoding=<encoding name>:
扩展资料:
执行python时,.py文件中的源代码首先编译成python的字节码,然后由python虚拟机执行编译后的字节码。该机制的基本思想与Java、.NET相一致。
但是,与Python虚拟机和Java或.NET虚拟机不同,Python的虚拟机是一种更高级的虚拟机。
这里的高级不是一般意义上的高级。不是Python的虚拟机比Java或.NET更强大,但是Python的虚拟机比Java或.NET更远离真实的机器。
或者这样说,python的虚拟机是一个更高层次的抽象虚拟机。由基于c的python编译的字节码文件,通常采用.pyc格式。
此外,python还可以在交互模式下运行。例如,主流操作系统unix/linux、mac和windows可以直接以命令模式运行python交互环境。互操作性可以通过直接发出操作命令来实现。
参考资料来源:百度百科-Python
python解释器的介绍:
解释器由一个编译器和一个虚拟机构成,编译器负责将源代码转换成字节码文件,而虚拟机负责执行字节码。
所以,解释型语言其实也有编译过程,只不过这个编译过程并不是直接生成目标代码,而是中间代码(字节码),然后再通过虚拟机来逐行解释执行字节码。
计算机的大脑是CPU, 中文名叫中央处理器,它仍然不能直接处理 Python 语言。CPU 只能直接处理机器指令语言,那是一种由0和1数字组成的语言,这是一种我们人很难直接写出来的语言。
所以,我们需要一个翻译,把Python语言翻译成 计算机CPU 能听懂的机器指令语言,这样计算机才能按照 我们的Python程序的要求去做事。.py结尾的文件需要解释器去运行执行。
执行过程原理:
1.执行 python XX.py 后,将会启动 Python 的解释器。
2.python解释器的编译器会将.py源文件编译(解释)成字节码生成PyCodeObject字节码对象存放在内存中。
3.python解释器的虚拟机将执行内存中的字节码对象转化为机器语言,虚拟机与操作系统交互,使机器语言在机器硬件上运行。
4.运行结束后python解释器则将PyCodeObject写回到pyc文件中。当python程序第二次运行时,首先程序会在硬盘中寻找pyc文件,如果找到,则直接载入,否则就重复上面的过程。
Python解释器执行程序的三个阶段:
第一步:先启动python3解释器。
第二步:Python3解释器就像一个文本编辑器一样将文件python3 D:\test.py从硬盘读入内存。
第三步:Python3解释器解释执行文件代码。
只有第三阶段才识别python的语法。