第二章 Python如何运行程序
本章和下一章将给出程序执行的简要说明
Python解释器简介
多数时都是将Python作为一门编程语言介绍的,但是,从目前的现实来讲,Python也是一个名为解释器的软件包(好像跟R语言差不多)。解释器是一个让程序运行起来的程序。当你编译了一段Python程序,Python解释器将读取程序,并按照其中的命令执行,得出结果。实际上,解释器是代码与机器的计算机硬件之间的软件逻辑层。(负责执行代码,让机器能明白代码的意思)
当Python包安装在机器上后。它会生成一些组件:至少包括一个解释器和一套支持库。根据使用情况的不同,Python解释器可能采取可执行程序的形式,或者是作为连接到另一个程序的一系列库。(要么自己解释,要么找别人)根据选用的Python版本不同,解释器本身可以用C程序实现,或一些Java类实现,或者其他形式。无论什么形式,编写的Python代码必须在解释器中运行。
程序执行
编写或运行Python程序的意义是什么呢?这取决于你是从程序员角度还是Python解释器的角度去看待这个问题。无论从哪一个角度来看问题,这都会提供给你一个观察Python编程的重要视角。
程序员视角
就最简单的形式而言,一个Python程序仅是一个包含Python语句的文本文件,例如,一个命名为script0.py的文件
这应该是最简单的Python脚本,但它是一个功能完整的Python程序。
这个文件包括两个Python的print语句,在输出流中简单的打印一个字符串(伟大的hello world)
和一个数学表达式的结果(2的100次方)。不用为这段代码的语法担心,以及为什么在Python中可以计算出2的100次方而不溢出
可以用任何自己喜欢的文本编译器创建由这样的语句所组成的文件。按照惯例,Python文件是以.py结尾的。从技术上讲,这种命名方案在被导入时才是必须的(导入概念下一章讲),但绝大多数Python文件为了统一都是以.py命名的
当你版这些语句输入到本地文件后,必须告诉Python去执行这个文件。也就是说,从头至尾按照顺序一个接一个的运行文件中的语句,正如下一章你将看到的,可以通过命令行,从IDE中去点击图标或者其他标准技术来运行费Python程序,顺利的话,在执行文件时,将会看到这两个打印语句的结果显示在屏幕的某处,一般默认是显示在运行程序的那个窗口(我这个不是,我用的IDLE)
对于一个新手,这对掌握一些程序执行的脚本概念已经足够了(图一乐也就图一乐)
Python的视角
上一节的简要介绍对于脚本语言是相当标准的,并且通常绝大多数Python程序员只需要知道这些就足够了。在文本文件中输入代码,之后再解释器中运行这些代码。然而,当Python运行时,透过表面,还有一些事情发生。尽管了解Python的内部并不是Python编程必须的需求,但是对于Python运行时结构有一些基本的了解可以帮助你从宏观上把握程序的运行
但Python运行脚本时,在代码开始进行处理之前,Python还会执行一些步骤。确切的说,第一步是编译成所谓的字节码。之后将其转发到所谓的虚拟机中
字节码编译
执行程序时,Python内部(对大多数用户是完全隐藏的)会将源代码(文件中的语句)编译成所谓的字节码形式。编译是一个简单的翻译步骤,字节码是一种低级的,与平台无关的表现形式,概括的说,Python通过把你的每一条源语言分解为单一步骤来翻译成一组字节码指令。这些字节码可以通过提高执行速度,比起文本文件中原始的源代码相比,字节码的运行速度要快的多。
上一段的所提到的这个过程对于你是完全隐藏起来的。如果Python进程在机器上有写入权限,暗恶魔它将把程序的字节码保存为一个以.pyc为扩展名的文件。在Python3.2以及以后的版本,Python将把.pyc字节码存储在名为__pycache__子目录的中,这个子目录位于与源文件相同的路径下,新版Python的__pycache__子目录中的文件命名中包含了编译它们的Python的版本信息,新的__pycache__子目录能够避免将太多文件挤在同一路径下,而新的字节码命名规范确保了同一台电脑上安装的不同版本的Python所生成的字节码不会相符覆盖。我们将会在22章中深入学习这种字节码文件模型,尽管他们是自动生成的,与大多数python程序无关,也随着不同版本的Python有着不同的形式。
Python这样保存字节码是作为对启动速度的一种优化,下一次运行程序时,如果你在商场保存字节码之后没有修改过源代码,并且运行的是同一个Python编译器的版本,那么Python将会加载.pyc文件并跳过编译这个步骤。工作原理如下:
源文件的改变:Python会自动检测源文件和字节码文件最后一次修改的时间戳,确认它是否必须重新编译,如果你编译后又保存了源代码,字节码将自动重新创建
Python的版本:导入机制同时会检测是否需要因为使用了不同的Python版本而重新编译
(一看时间戳,二看版本)
其结果就是源文件的修改和Python版本的改变都会触发新的字节码文件的编译。
如果Python无法在机器上写入字节码,程序仍然可以工作,字节码会在内存中生成,并在程序结束时直接丢弃,然而,由于.pyc文件能够加速启动,最好保证大型程序中能够创建。
如果Python找到的都是.pyc文件,它也很乐意运行这个程序,即便没有原始的.py源代码文件(一会讲‘冻结二进制文件’一节来解决其他程序发布的选项)
最后请牢记,字节码只会只对那些被导入(import)的文件而生成,而不是顶层的执行脚本(严格来说,这是一种针对导入的优化)。此外,文件仅在程序执行(或者可能编译)时才会导入,而在交互式命令行中输入的指令不会输入字节码。这种利用交互式命令行的编程模式会在第三章讨论
Python虚拟机(PVM)
一旦程序编译成字节码或者字节码从已经存在的.pyc文件中载入,之后的字节码将发送到通常称为Python虚拟机PVM的程序上来执行。实际上,他不是一个独立的程序,不需要安装。本质上,PVM就是迭代运行字节码指令的一个大循环,一个接一个的完成操作。PVM是Python的运行时引擎,它时常表现为Python系统的一部分,并且是实际运行脚本的组件。从技术上讲,他是Python解释器的最后一步
图2-2展示了这里介绍的运行时结构。请记住这些复杂性都是有意的对Python程序员进行隐藏的。字节码的编译是在自动完成的,而PVM也是仅仅安装在机器上的Python系统的一部分,所以,了解就好
性能的含义
熟悉C和C++的=这类完全编译语言的读者或者已经发现Python模式中的一些不同之处,其中之一便是,扎起Python的工作中通常没有构建或者make的步骤;代码在写好后立即运行,另一个就是,Python字节码不是机器的二进制代码,字节码是特定于Python的一种表现形式。
图2-2是Python的传统运行时执行模型,你输入的源代码转换为字节码,之后字节码在Python虚拟机运行。代码自动会被编译,之后再被解释
这就是Python代码无法运行的像C或者C++一样快的原因,就像第一章描述的那样,PVM循环(而不是CPU芯片)仍需解释字节码,并且字节码指令比CPU指令需要更多的工作,另一方面,与其它经典的解释器不同,这里仍然有内部编译的步骤:Python并不需要反复的重复分析和分解每一行源代码语句的文本,最终效果就是纯Python代码的运行速度会介于传统的编译语言和传统的解释语言之间
开发的含义
Python执行模型所导致的另一个结果是其开发和执行的环境实际上并没有区别,也就是说,编译和执行源代码的系统是同一个系统·。在Python中,编译器总是在运行时出现,并且是运行程序系统的一部分
这将会大大缩短开发周期,程序在执行之前并不需要预编译和链接;只需要简单的输入并运行代码即可,这同样让Python带上了更浓厚的动态语言色彩,在运行时,Python程序去构建并执行另一个Python程序是有可能的,而且往往是非常方便的,如:eval和exec内置模块能够接受并运行包含Python程序代码的字符串。这种结构是Python能够实现产品定制的原因,因为Python代码可以动态的被修改,用户可以改进系统内部的Python部分,而不需要拥有整个或编译整个系统的代码
从更基础的角度来说,牢记我们在Python中真正拥有只有运行时:完全不需要初始的编译阶段,所有的事情都是在程序运行时发生的。这甚至还包括了建立函数和类的操作以及模块的链接。这些事情对于静态语言往往是发生在执行之前,而在Python中是与程序的执行同时进行的。
执行模型的变体
上一节介绍了内部的执行流程,而这种流程也反映了今天Python的标准实现形式,但这实际上并不是python语言本身所必须的。正因为这样待你,执行模型也会随着时间而改变。事实上,现在已经出现一些系统修改了图2-2所描述的情况,
Python的各种实现
据我所知,Python语言目前主要有五种实现方式
CPython:标准Python
最初的,标准的Python实现方式通常被称为CPython,这个名字来源于它是由可移植的ANSIC语言代码编写而成的这个事实。这就是你从http://www.python.org获取的,从ActivePython和Enthought的发行版中得到的,以及绝大多数Linux和Mac OS X机器上自动安装的Python。与其他的替代系统相比,他的运行速度更快,最完整,最新,而且也最健全。
JPython:基于Java的Python
目标是让Python代码能够脚本化Java应用程序。它实现了与Java的无缝集成。系统本质是将图2-2最右边的两个方框(我画的圆)中的内容替换为基于java的等效实现。,由于python代码被翻译为Java字节码,因此在运行时看起来就像真正的Java程序一样
IronPython:基于.NET的Python
其设计目的是让Python程序可以与Windows平台上的.NET框架以及与之对应的LLinux上开源的Mono编写的应用相集成,IronPython如需Python程序同时扮演客户端和服务器端的角色,与.NET语言进行来回访问,以及在Python代码中利用诸如Silverlight框架的.NET技术
Stackless:注重并发的Python
针对并发性而优化的一个增强版本实现,不会再C语言调用栈上保存状态,这使得更容易移植到较小的栈构架中,提供了更高效的多处理选项,并且促进了向协程这样的新颖的编程结构的出现
PyPy:注重速度的Python
我啃的这本书虽说是最新版,去年出版,但也正是以Python3.3写的,书中介绍pypy支持2.7,还未支持3.X,查了下百度百科,最新的(2018.12.15)PyPy版本是PyPy3 v6.0.0,兼容Python3.5.3。可以运行在Linux的32位和64位、MacOSX和Windows的32位平台中。
现在Python到了3.8,能不能用上不好说。
pypy是CPython标准的另一个实现,它更注重性能,它提供了一个带有即时编译器的Python快速实现,能够在安全环境中运行不信任代码的“沙箱”模型的工具,
PyPy是原先的Psyco即时编译器的继任者,并将Psyco纳入一个追求速度的纯Python实现。及时编译器事实上只是PVM的一个扩展,它将你的字节码中的部分直接转换为运行速度更快地二进制机器码。这一切发生在你的程序运行阶段,而非运行前的编译阶段。
21章的时候我们还会见到这个(如果有时间最近啃写这本书的话(庆余年真好看))
执行优化工具
CPython和前一节介绍的大部分实现变体都是以相似的方式实现Python语言的,通过吧源代码编译为字节码,然后在合适的虚拟机上执行这些字节码。还有一些其他的系统,包括Cython混合语言,Shed Skin C++转换器,以及PyPy与Psyco中的即时编译器,则试着优化了基本的执行模块,这些系统并不是你现阶段学习Python的必备知识,但是简要的了解他们在执行模型中所处的位置能够帮助你解开执行模型的神秘面纱
Cython:Python和C的混合体
它为Python代码配备了调用C函数以及使用变量、参数、和类属性声明的能力。Cython代码可以编译成使用python/C API 的C代码,最后可以再完成编译,尽管与标准Python并不完全兼容,但对于包装外部的C库以及提高Python的C扩展的编码效率都很有用
Shed Skin:Python到C++的转换器
这是一个新兴的系统,它采用了一种不同的Python程序执行方式,尝试将Python代码翻译为C++代码,然后使用机器中的C++编译器将得到的C++代吗编译为机器代码。通过这种方式,它已一种与平台无关的方式来运行Python代码
我上当了,好像不支持Python3.X
Psyco:原先的及时编译器
书上一大长串子,不想写了。
过时了,要淘汰了,淹没在历史的洪流里了,PyPy是它的继任者,你说什么时候的吧。
这是一个能够让程序运行的更快的扩展字节码执行的模块组件
冻结二进制文件
有时候人们需要一个真正的Python编译器,实际上他们需要的是一种能让python里的程序生成独立的二进制可执行代码的简单方法。这是一个比执行流概念更接近于打包发布的概念,但是两者之间或或少有些联系。你可以通过网络获取第三方工具将Python转为可执行程序,他们在pPython世界中称为冻结二进制文件,这些程序可以不安装Python环境而独立运行
冻结二进制文件能够将程序文件的字节码,PVM以及任何程序所需的Python支持文件捆绑在一起形成一个单独的文件包,这个过程存在着一些不同的变体,但最终的结果是一个可执行的二进制程序,如Windows的exe文件
冻结二进制文件与真实的编译器输出结果有所不同,他们通过虚拟机运行字节码,因此,除了必要的·初始改进,冻结二进制文件和最初的源代码程序运行速度完全相同。冻结二进制文件并不小(有个PVM),但是以现在的标准来看,其实也不是特别大。
对于接受着,代码都是隐藏起来的,并且无需安装,可以直接运行。
未来的可能性
尽管未来的实现方式有可能从某种程度上改变Python的运行时结构,但就接下来一个时期来看,字节码编译仍然将会是唯一标准,字节码的可移植性和运行时的灵活性对于很多python系统来说是很重要的特性,此外,未来实现静态编译,而增加类型约束声明将会破坏这种灵活性,紧凑,简明以及所有代表Python编码精神的特性。由于Python本身高度动态的本质,今后的任何实现方式都可能保留诸多当前的PVM的产物。
本章小结
唠一下这一章学了什么吧,什么是Python解释器,运行Python程序的程序;Python将源代码翻译成字节码,字节码能跨平台,所有Python有很好的移植性,又因为是字节码,所以性能不如C的二进制机器码,字节码存储扎起.pyc文件中,文件名有Python的版本号,.pyc带有时间戳确定是否修改,在版本改变时或者源文件修改并与.pyc文件时间戳不同时字节码文件就是那个.pyc文件修改,字节码文件比源代码执行速度快,因为少了一步转换为字节码,在运行时,字节码文件发送到虚拟机中,负责解释编译字节码。源代码到字节码再到PVM,执行过程只有执行时,不需要预编译和链接。大大缩短开发周期。
为了各种各样的事情,如配合JAVA,提升速度,多进程等,出现各种实现方式,其中CPython是标准实现,其他实现方式都是改变字节码,虚拟机。
还有一些执行优化工具,但书上介绍几个对于Python3.X的支持不太好,就不说了。
冻结二进制文件,就是将Python程序转换为可执行程序,将一堆东西打包,字节码文件,pvm,还有python的相关支持文件合在一起,然后变成一个不需要安装,可以直接执行的程序,接收者库看不见代码(不要源代码,有字节码够用了),不要Python环境(我打包进去了)。
未来优化方向如果要保留现在的Python特性应该没什么可以改的了(应该)
然后是抄写并背诵本章习题
本章习题
1.什么是Python解释器?
Python解释器是运行你所编写的Python程序的程序。
2.什么是源代码?
源代码是为程序所写的语句:它由文本文件中的文本组成,通常以.py作为后缀名。
3.什么是字节码?
字节码是Python将程序编译后所得到的低级形式。Python将字节码自动保存到后缀名为.pyc的文件中。
4.什么是PVM?
PVM是Python的虚拟机,他是Python的运行时引擎,能解释编译得到的字节码。
5.请列举两个或多个Python标准执行模型的变体的名字。
Psyco、Shed Skin以及冻结二进制文件都是执行模型的变体。除此之外,在接下来的两个问题中提到的其他Python实现,以代替字节码和虚拟机或是添加工具和JIT的形式对Python的执行模型进行时改进。
6.CPython。JPython以及IronPython有什么不同?
CPython是Python语言的标准实现。JPython和IronPython分别是Python程序在Java和.NET环境的实现,它们都是Python的代替编译器。
7.什么是Stackless和PyPy?
Stackless是一种针对并发性而增强的Python版本,而PyPy是针对速度而增强的Python实现。PyPy作为Psyco的继任者,融合了Psyco中的JIT概念。
(下一章的东西好多啊,第一次读的时候感觉都是废话,第二次感觉实在太多坑了,我准备写下一章前又读一次感觉又都是废话,许多坑都是如果你不知道会直接放弃学习(开局直接裂开了,玩不下去了,就好像安装完环境写下hello world直接报错了,学个屁啊),知道了那它啥也不是。下一章两万字打底把,我太难了)