主要参考:
python解释器
python是一门解释型语言,但还不够确切,其实许多解释型语言在正式处理代码前,都是先需要内部进行预处理的。
Python解释器由编译器和虚拟机构成。
执行 Python 程序(.py文件)时,Python 内部先隐式地将源代码 (source code) 编译成字节码。这一步我们会发现生成了一种.pyc文件,.pyc文件中存储着的就是python程序编译后的字节码。这种.pyc文件具备可移植性,不管在哪一个平台都可以使用。运行时将由虚拟机逐行把字节码翻译成机器码,这才能被计算机直接执行。
解释器是一种让其他程序运行起来的程序,它是代码与机器的计算机硬件之间的软件逻辑层,Python解释器就是能够让Python程序在机器上执行的一套程序。 当我们执行写好的Python代码时,Python解释器会执行两个步骤
1、
把原始代码编译成字节码
编译后的字节码是特定于Python的一种表现形式,它不是二进制的机器码,需要进一步编译才能被机器执行,这也是Python代码无法运行的像C/C++ 一样快的原因。如果Python进程在机器上拥有写入权限,那么它将把程序的字节码保存为一个以.pyc 为扩展名的文件,如果Python无法在机器上写入字节码,那么字节码将会在内存中生成并在程序结束时自动丢弃。在构建程序的时候最好给Python赋上在计算机上写的权限,这样只要源代码没有改变,生成的.pyc文件可以重复利用,提高执行效率。
2、
把编译好的字节码转发到Python虚拟机(PVM)中进行执行
PVM是 Python Virtual Machine的简称,它是Python的运行引擎,是Python系统的一部分,它是迭代运行字节码指令的一个大循环、一个接一个地完成操作。
Python 解释器的几种实现版本
Python解释器有三种主要的实现方式,CPython、Jython和IronPython 三种实现方式 。
1、
CPython
CPython 是标准的实现,其它的都是有特定目标的。 CPython 是由C语言编写的,它是大多数Linux和Mac OS X机器预装的Python解释器,也是所有Python解释器中运行最快、最完整、最健全的。
2、
Jython
Jython 是一种Python语言的替代实现方式,其目的是为了与Java编程语言集成,Jython 包含了Java类,这些类编译Python源代码、形成Java字节码,并将得到的字节码映射到Java虚拟机(JVM)上。因为Jython要比CPython 慢而且也不够健壮,它往往看作是一个主要面向寻找Java代码前端脚本语言的Java开发者的一个有趣的工具。
3、
IronPython
IronPython 设计的目的是让Python 程序可以与Windows 平台上的.NET 框架以及与之对应的Linux的上开源的Mono编写成的应用集成。
4、
IPython
IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,但是执行Python代码的功能和CPython是完全一样的。好比很多国产浏览器虽然外观不同,但内核其实都是调用了IE。
CPython用
>>>
作为提示符,而IPython用In [序号]:
作为提示符。5、
PyPy
PyPy是另一个Python解释器,它的目标是执行速度。PyPy采用JIT技术,对Python代码进行动态编译(注意不是解释),所以可以显著提高Python代码的执行速度。
绝大部分Python代码都可以在PyPy下运行,但是PyPy和CPython有一些是不同的,这就导致相同的Python代码在两种解释器下执行可能会有不同的结果。如果你的代码要放到PyPy下执行,就需要了解PyPy和CPython的不同点。
Psyco 实时编译器
psyco 系统不是Python的另一种实现方式,而是Python字节码执行模块的一个扩展组件,它可以让程序运行的更快。它是一个PVM的增强工具,这个工具收集并使用信息,在程序运行时,可以将部分程序的字节码转换成底层的真正的二进制机器代码,从而实现更快的执行速度。在开发个过程中,Psyco无需代码的修改或独立的编译步骤即可完成这一转换。 概括地讲,就是当程序运行时,Psyco收集了正在传递过程中的对象的类别信息,这些信息可以用来裁剪对象的类型,从而生成高效的机器代码。机器代码一旦生成,就替代了对应的原始字节码,从而加快程序的整体执行速度。一些通过Psyco优化的Python代码的执行速度可以像编译好的C代码一样快。 因为字节码的转换与程序运行同时发生,所以Pysco往往被看做是一个即时编译器(JIT)。实际上Psyco是一个专有的JIT编译器:它生成机器代码将数据类型精简至你程序实际上所使用的类型。 Psyco是用纯Python的算法代码实现的。这点尤为重要,那些为了优化往往需要迁移到C的那部分代码,使用了Psyco后,这样的迁移就没有必要了。
Python解释器将源码转换为字节码,然后再由解释器来执行这些字节码。因此总的来说,它具有以下三条特性
源码距离底层更远(根据官方文档的解释)
运行时都需要生成字节码,交由虚拟机执行。(就是解释器。虚拟机具体实现了由switch-case语句构成的框架函数PyEval_EvalFrameEx,刚刚说的字节码就是这货执行的)
每次执行脚本,虚拟机总要多出加载和链接的流程。(所以呢,相比于编译型语言就有点慢了
虚拟机它是怎么执行脚本的
完成模块的加载和链接;
将源代码翻译为PyCodeObject对象(这货就是字节码),并将其写入内存当中(方便CPU读取,起到加速程序运行的作用);
从上述内存空间中读取指令并执行;
程序结束后,根据命令行调用情况(即运行程序的方式)决定是否将PyCodeObject写回硬盘当中(也就是直接复制到.pyc或.pyo文件中);
之后若再次执行该脚本,则先检查本地是否有上述字节码文件。有则执行,否则重复上述步骤。
更多参考:
python程序从哪里开始执行
参考:python-从哪里开始执行?_python程序是从哪里开始运行的-CSDN博客
如果你经常使用的是C语言或者其他高级语言,当你去开始阅读代码的时候,你总会不自觉的去找main函数,但是当你初次接触Python的时候可能会感觉到不习惯,因为Python没有main主函数。
- python 代码的执行不依赖于 main()函数;
- python 代码从没有缩进的代码开始执行。
python使用缩进对齐组织代码的执行,所有没有缩进的代码(非函数定义和类定义),都会在载入时自动执行,这些代码,可以认为是Python的main函数。
每个文件(模块)都可以任意写一些没有缩进的代码,并且在载入时自动执行,为了区分主执行文件还是被调用的文件,因此python就引入了一个机制。
if __name__ == "__main__":
如果我们在程序里面添加如上的代码,编译器就能识别出从哪里开始。这句话的含义:如果这个文件被别的文件当作模块调用了,那么此时name 就会变为文件的名字,否则默认是main,那么整个工程就会从这句话下面的没有缩进的代码开始执行。说的可能比较拗口,直接看个例子吧。
if __name__ == "__main__": print("Hello World!") def test(): print("test") def main(): print("main") main()
执行结果
Hello World! main
可以看到代码先打印的不是main因为,程序从if name == “main“:下面没有缩进的代码开始执行,类和函数不在内。先打印了Hello World!,然后执行main函数打印出main;
再看看另一篇文章:「Python 」 python 从哪开始执行 之 main 函数_python程序从main开始-CSDN博客
初次接触Python的人会很不习惯Python没有main主函数。 这里简单的介绍一下,在Python中使用main函数的方法
#hello.py def foo(): str="function" print(str); if __name__=="__main__": print("main") foo()
其中if __name__=="__main__":这个程序块类似与Java和C语言的中main(主)函数
在Cmd中运行
C:\work\python\divepy>python hello.py main function
在Python Shell中运行
>>> import hello >>> hello.foo() function >>> hello.__name__ 'hello' >>>
可以发现这个内置属性__name__自动的发生了变化。 这是由于当你以单个文件运行时,__name__便是__main__ 当你以模块导入使用时,这个属性便是这个模块的名字。
在C/C++/Java中,main是程序执行的起点,Python中,也有类似的运行机制,但方式却截然不同:Python使用缩进对齐组织代码的执行,所有没有缩进的代码(非函数定义和类定义),都会在载入时自动执行,这些代码,可以认为是Python的main函数。
每个文件(模块)都可以任意写一些没有缩进的代码,并且在载入时自动执行,为了区分主执行文件还是被调用的文件,Python引入了一个变量__name__,当文件是被调用时,__name__的值为模块名,当文件被执行时,__name__为'__main__'。这个特性,为测试驱动开发提供了极好的支持,我们可以在每个模块中写上测试代码,这些测试代码仅当模块被Python直接执行时才会运行,代码和测试完美的结合在一起。
总结:
1). python 代码的执行不依赖于 main() 函数;
2). python 代码从没有缩进的代码开始执行。
例子1:
$ cat main.py #! /usr/bin/env python """ @file main.py @brief main.py for testing python main entry. """ import os,sys import time if __name__ == "__main__": print("Hello python !!!") def func_print(): print("in func_print") def main(): print("In main") main()
运行结果
$ ./main.py Hello python !!! In main
例子2:
$ cat main2.py #! /usr/bin/env python """ @file main.py @brief main.py for testing python main entry. """ import os,sys import time print("Hello python !!!") def func_print(): print("in func_print") def main(): print("In main") #main() func_print()
运行结果:
$ ./main2.py Hello python !!! in func_print
疑问:import会执行吗?
可参考:python导入包以及Python程序执行顺序理解_from config.config import *-CSDN博客
对于一个Python程序,首先执行的是import程序,特别地,如果import了别的包,那么会跳入到别的包模块先执行。
import os # 1 print('<[1]> time module start') # 2 class ClassOne(): print('<[2]> ClassOne body') # 3 def __init__(self): # 10 print('<[3]> ClassOne.__init__') def __del__(self): print('<[4]> ClassOne.__del__') # 101 def method_x(self): # 12 print('<[5]> ClassOne.method_x') class ClassTwo(object): print('<[6]> ClassTwo body') # 4 class ClassThree(): print('<[7]> ClassThree body') # 5 def method_y(self): # 16 print('<[8]> ClassThree.method_y') class ClassFour(ClassThree): print('<[9]> ClassFour body') # 6 def func(): print("<func> function func") if __name__ == '__main__': # 7 print('<[11]> ClassOne tests', 30 * '.') # 8 one = ClassOne() # 9 one.method_x() # 11 print('<[12]> ClassThree tests', 30 * '.') # 13 three = ClassThree() # 14 three.method_y() # 15 print('<[13]> ClassFour tests', 30 * '.') # 17 four = ClassFour() four.method_y() print('<[14]> evaltime module end') # 100
- 首先执行#1的import语句
- 执行#2的打印语句
- ClassOne、ClassThree和ClassFour的类定义执行过程中,分别打印#3、#4、#5、#6四句话,但是其中的方法并不执行,仅仅是载入内存
- 碰到#7的if name == ‘main’:,判断为True,于是执行if内部的代码
- 执行#8的print语句
- 执行#9,实例化一个ClassOne的对象
- 执行#10的初始化方法,打印一条语句
- 返回执行#11的menthod_x调用
- 返回类的定义体,找到#12,执行方法,打印语句
- 再返回#13处,打印
- 执行#14的实例化
- ClassThree没有自定义初始化方法,接着执行#15
- 回到类里找到#16的方法,执行打印语句
- 执行#17
…后面不再详述
执行完最后的#100的打印语句后,按理说程序应该终止退出了,但由于ClassOne这个类定义了__del__方法,还要在最后执行它内部的代码#101这条打印语句。
通过这个例子,相信你对Python的程序执行流程能够有一定的了解。其实这个过程,也是我们读别人代码的过程。
更多补充:python import导入时,发生了什么?_python import一个包时发生了哪些操作-CSDN博客
python在导入模块时,即import时究竟有哪些动作?在python中,导入并非只是把一个文本文件插入到另一个文件。导入其实是运行时的运算,程序第一次导入指定文件时,会执行以下三个步骤:
1、找到模块文件
2、编译成位码(即pyc文件)
3、执行模块的代码来创建其所定义的变量(你没看错,导入时代码是会执行的)
需要明确的事,模块导入只有在第一次导入时才会进行。此后,导入相同模块时,会跳过这三个步骤,而只提取内存中已经加载的模块对象。从技术上,Python把载入的模块储存在一个名为sys.modules的表中,并在第一次导入操作时,检查该表。如果不存在便会启动这三个过程。
小结
虽然我们有各种流程判断、循环、跳转、控制、中断等等,但从根本上程序还是逐行顺序执行的。
Python代码在执行过程中,遵循下面的基本原则(可参考计算机系统相关知识理解):
普通语句,直接执行;
碰到函数,将函数体载入内存,并不直接执行
碰到类,执行类内部的普通语句,但是类的方法只载入,不执行
碰到if、for等控制语句,按相应控制流程执行
碰到@,break,continue等,按规定语法执行
碰到函数、方法调用等,转而执行函数内部代码,执行完毕继续执行原有顺序代码
内置模块if name=='main’
在一个py程序中,经常会看到if name==‘main’;其中__name__本身是一个内置模块,py文件通常有两大类执行方式,要么是直接运行,比如直接在IDE窗口中点run或者Python 目标.py;要么是被调用运行;在直接运行的时候,该程序的内置属性__name__的值就会是__main__;但py文件作为被调用的对象的时候,该值就不为__main__;