谈到了Python语句的两种执行方式,实际上,这两种运行方式在本质 上是相同的,它们都是由解释器来解释执行我们提供的Python语句。 这里所说的解释执行是相对于编译执行而言的。我们知道,使用诸如 C或C++之类的编译性语言编写的程序可以从源文件转换成计算机使用 的机器语言, 经连接器连接后形成二进制可执行文件。当我们运行二进制可执行程 序的时候,因为已经编译好了,所以加载器软件把二进制程序从硬盘 载入内存中并运行。
与之不同,Python语言写的程序不需要编译成二进制代码,它可以直 接从源代码运行程序。当我们运行Python文件程序的时候, Python解释器把源代码转换成中间形式:字节码,然后再由Python虚 拟机来执行这些字节码,如图5所示。 这样的话,我们就用不着担心程序的编译,库的连接加载等问题了, 所有这些工作都由Python虚拟机代劳了。 对于Python的解释语言特性,我们要一分为二的看待。一方面,每次 运行时都要进行转换成 字节码,然后再由虚拟机把字节码转换成机器语言,最后才能在硬件 上运行。较之于编译性 编程 语言,每次运行都会多出两道工序,所以它的性能会受到影响。另一 方面,由于不用关心程 序的编译以及库的连接等问题,所以开发工作会变得更轻松;同时虚 拟机距离物理机器更远 了, 所以Python程序更加易于移植,实际上无需改动就能在多种平台上运 行。
1. 在具体计算机上实现一种语言,首先要确定的是表示该语言语义解释 的虚拟计算机,一个关键的问题是程序执行时的基本表示是实际计算 机上的机器语言还是虚拟机 的机器语言。这个问题决定了语言的实现。根据这个问题的回答,可 以将程序设计语言划分为两大类:编译型语言和解释型语言。
2. 由编译型语言编写的源程序需要经过编译、汇编和链接才能输出目标 代码,然后机器执行目标代码,得出运行结果,目标代码由机器指令 组成,一般不能独立运行, 因为源程序中可能使用了某些汇编程序不能解释引用的库函数,而库 函数代码又不在源程序中,此时还需要链接程序完成外部引用和目标 模块调用的链接任务,最后 输出可执行代码。C、C++、Fortran、Pascal、Ada都是编译实现的。
3. 解释型语言的实现中,翻译器并不产生目标机器代码,而是产生易于 执行的中间代码,这种中间代码与机器代码是不同的,中间代码的解 释是由软件支持的,不能直接 使用硬件,软件解释器通常会导致执行效率较低。用解释型语言编写 的程序是由另一个可以理解中间代码的解释程序执行的。与编译程序 不同的是,解释程序的任务 是逐一将源程序的语句解释成可执行的机器指令,不需要将源程序翻 译成目标代码后再执行。对于解释型Basic语言,需要一个专门的解 释器解释执行 Basic程序,每条语言只有在执行才被翻译。这种解释型语言每执行 一次就翻译一次,因而效率低下。
4. Java很特殊,Java程序也需要编译,但是没有直接编译称为机器语言 ,而是编译称为字节码,然后在Java虚拟机上用解释方式执行字节码 。Python 的也采用了类似Java的编译模式,先将Python程序编译成Python字节 码,然后由一个专门的Python字节码解释器负责解释执行字节码。
(Java虚拟机对字节码的执行相当于模拟一个cpu,而ruby1.8--在虚 拟机还未出现前--是通过解释成语法树执行。)
Python是一个解释型的语言。但是为了效率上的考虑,Python也提供 了编译的方法。编译之后是bytecode的形式。Python也提供了和Java 类似的VM来执行这样的bytecode。不同的是,因为Python是一个解释 型的语言,所以编译(compile)不是一个强制的操作。事实上,编 译是一个自动的过程。多数情况下,你甚至不会在意他的存在。编译 成bytecode可以节省加载模块的时间,从而提高效率
除了效率原因,bytecode也增加了反向工程的难度,在某种程度上保 护你的代码。当然,反编译仍然是可以的。所以如果真的想保护代码 ,还是用别的方法吧。另外Python还提供了-O选项,可以编译生成“优化”的bytecode,文 件扩展名是.pyo。但实际上优化的内容有限,作用不大。如果希望生成可执行文件,就要依赖于第三方的工具了。