Python 是一门解释型的编程语言,因此它具有解释型语言的运行机制。
知道Python运行机制,并不是我们一般人所必须的。但是,了解其加速程序运行以及优化代码的设计思想,对于我们在日后构造缓存系统、如何减少不必要的运行时间,以及同步更新工作内容等问题上起到很大的借鉴作用。
计算机程序,其实就是一组计算机指令集,能真正驱动机器运行的是机器指令,但让普通开发者直接编写机器指令是不现实的,因此就出现了计算机高级语言。
- 高级语言允许使用自然语言(通常就是英语)来编程,但高级语言的程序最终必须被翻译成机器指令来执行。
高级编程语言按程序的执行方式可以分为编译型和解释型两种。
现有的 C 、C++、Objective-C、Pascal 等高级语言都属于编译型语言。
编译型语言是指使用专门的编译器,针对特定平台(操作系统)将某种高级语言源代码一次性“翻译”成可被该平台硬件执行的机器码(包括机器指令和操作数),并包装成该平台所能识别的可执行程序的格式,这个转换过程称为编译(Compile)。
- 有些程序编译结束后,还可能需要对其他编译好的目标代码进行链接,即组装两个以上的目标代码模块生成最终的可执行程序,通过这种方式实现低层次的代码复用。
- 编译生成的可执行程序可以脱离开发环境,在特定的平台上独立运行。
- 因为编译型语言的程序被编译成特定平台上的机器码,因此编译生成的可执行程序通常无法移植到其他平台上运行,如果需要移植,则必须将源代码复制到特定平台上,针对特定平台进行修改,至少需要采用特定平台上的编译器重新编译。
解释型语言是指使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行的语言。
Python 语言属于解释型语言,因此运行 Python 程序时需要使用特定的解释器进行解释、执行。
*什么是解释器?
解释器是一种让其他程序运行起来的程序,它是代码与机器的计算机硬件之间的软件逻辑层。
- Python解释器本身也是个程序, 它是解释执行 Python代码的,所以叫解释器。
解释器由一个编译器和一个虚拟机构成。
所以,解释型语言其实也有编译过程,只不过这个编译过程并不是直接生成目标代码,而是中间代码(字节码),然后再通过虚拟机来逐行解释执行字节码。
CPython 具体内容如下:
- Scanner 对应词法分析器,将从文件输入的代码切分为 token
- Parser 对应语法分析器,在Scanner 的分析结果上进行语法分析,建立 AST
- Compiler 根据建立的 AST 生成指令集合 Python 字节码 (这里的 .pyc 文件类似于Java 的 .class 文件)
- 最后由 Code Evaluator 执行代码
- Jython 是把 Interpreter 给重写了,Jvm 此时就是 Code Evaluator
- 解释器中的 Code Evaluator 就是通常所说的 pvm (Python虚拟机)
1、执行 python XX.py 后,将会启动 Python 的解释器。
2、python解释器的编译器会将.py源文件编译(解释)成字节码生成PyCodeObject字节码对象存放在内存中。
3、python解释器的虚拟机(PVM)将执行内存中的字节码对象转化为机器语言,虚拟机与操作系统交互,使机器语言在机器硬件上运行。
Python虚拟机的原理就是模拟可执行程序在X86机器上的运行。
- 当发生函数调用时,创建新的栈帧,对应Python的实现就是PyFrameObject对象。
- PyFrameObject对象创建程序运行时的动态信息,即执行环境。
- 每一个 PyFrameObject对象都维护了一个 PyCodeObject对象,这表明每一个 PyFrameObject中的动态内存空间对象都和源代码中的一段Code相对应。
4、程序运行结束后,根据命令行调用情况(即运行程序的方式)决定是否将PyCodeObject写回硬盘当中(也就是直接复制到.pyc或.pyo文件中)。
Python解释器有三种主要的实现方式,CPython、Jython和IronPython 三种实现方式。
CPython 是由C语言编写的,它是大多数Linux和Mac OS X机器预装的Python解释器,也是所有Python解释器中运行最快、最完整、最健全的。
Jython 是一种Python语言的替代实现方式,其目的是为了与Java编程语言集成。
IronPython 设计的目的是让Python 程序可以与Windows 平台上的.NET 框架以及与之对应的Linux的上开源的Mono编写成的应用集成。
包:一个文件夹,用来存放模块和子包。
模块: 可以作为模块的文件有 .py 、.pyc、.pyo、.pyd、.so、.dll文件。
pyc是一种二进制文件,是由py文件经过编译后生成的文件,是一种byte code。
pyc文件其实是PyCodeObject的一种持久化保存方式。
- 文件中包含python的magic number(来说明编译时使用的python版本号)、源文件的mtime(使pyc和py文件保持同步)、编译出的code对象。
*什么是PyCodeObject?
- Python代码的编译结果就是PyCodeObject对象。
- PyCodeObject对象中包含了字节码指令以及程序的所有静态信息,但没有包含程序运行时的动态信息——执行环境(PyFrameObject)。
- 字节码在python虚拟机程序里对应的是PyCodeObject对象。
因为py文件是可以直接看到源码的,如果是开发商业软件的话,不可能把源码泄漏出去,所以就需要编译为pyc后,再发布出去。
pyc文件只有在文件被当成模块导入时才会生成,即模块加载的时候(import)。
也就是说,Python解释器认为,只有import进行的模块才需要被重用。
主文件一般只需要加载一次不会被其他模块导入,所以一般主文件不会生成pyc文件:
加载模块时,如果同时存在.py和.pyc,python会使用.pyc运行:
对于单个文件,使用方法非常简单,如下所示,xxx.py是需要编译的python源文件:
import py_compile # py_compile是Python的自带模块 py_compile.compile('xxx.py') # py_compile.compile(file[, cfile[, dfile[, doraise]]])可将.py文件编译生成.pyc文件(默认)
对于多个文件一般来说:
import compileall compileall.compile_dir(r'/Users/xxx/PythonFiles/')
直接通过命令来运行:
python3 -m py_compile ****.py //-m 表示把后面的模块当成脚本运行 python3 -O -m py_compile ****.py //-O 优化成字节码
可以看到命令中并没有用到compile()函数,这是因为py_compile模块的main()函数中调用了compile()。
python3 -m py_compile ****.py
其效果等效于如下代码:
import py_compile py_compile.compile('****.py') # 也可以是包含.py文件的目录路径 #此处尽可能使用raw字符串,从而避免转义的麻烦。比如,这里不加“r”的话,你就得对斜杠进行转义
如果你想看compile(), compile_dir(), compile_path()具体每个参数是干吗用的,可以使用print py_compile.compile().__doc__来查看,或者直接打开py_compile.py,compileall.py文件来看。
我们可以将里边的文件copy出来,这时候可以删除对应的.py文件,同时需要将xxx.pyc文件的名字进行修改。
例如: python 的.py文件叫 main.py,生成的.pyc文件叫main.cpython-36.pyc,在我们使用该模块的时候,需要将main.cpython-36.pyc修改为main.pyc,这里的“cpython-36”表示的是我在python 3.6 的环境下编译的。
【部分内容参考自】