python编译过程和执⾏原理python虚拟机
python编译过程和执⾏原理
(1)python执⾏原理
这⾥的解释执⾏是相对于编译执⾏⽽⾔的。我们都知道,使⽤C/C++之类的编译性语⾔编写的程序,是需要从源⽂件转换成计算机使⽤的机器语⾔,经过链接器链接之后形成了⼆进制的可执⾏⽂件。运⾏该程序的时候,就可以把⼆进制程序从硬盘载⼊到内存中并运⾏。
但是对于Python⽽⾔,python源码不需要编译成⼆进制代码,它可以直接从源代码运⾏程序。当我们运⾏python⽂件程序的时
候,python解释器将源代码转换为字节码,然后再由python解释器来执⾏这些字节码。这样,python就不⽤担⼼程序的编译,库的链接加载等问题了。
对于python解释语⾔,有以下3⽅⾯的特性:
1. 每次运⾏都要进⾏转换成字节码,然后再有虚拟机把字节码转换成机器语⾔,最后才能在硬件上运⾏。与编译性语⾔相⽐,每次多出
了编译和链接的过程,性能肯定会受到影响;⽽python并不是每次都需要转换字节码,解释器在转换之前会判断代码⽂件的修改时间是否与上⼀次转换后的字节码pyc⽂件的修改时间⼀致,若不⼀致才会重新转换。
2. 由于不⽤关⼼程序的编译和库的链接等问题,开发的⼯作也就更加轻松啦。
3. python代码与机器底层更远了,python程序更加易于移植,基本上⽆需改动就能在多平台上运⾏。
在具体计算机上实现⼀种语⾔,⾸先要确定的是表⽰该语⾔语义解释的虚拟计算机,⼀个关键的问题是程序执⾏时的基本表⽰是实际计算机上的机器语⾔还是虚拟机的机器语⾔。这个问题决定了语⾔的实现。根据这个问题的回答,可以将程序设计语⾔划分为两⼤类:编译型语⾔和解释型语⾔。
1. 编译实现的语⾔,如:C、C++、Fortran、Pascal、Ada。由编译型语⾔编写的源程序需要经过编译,汇编和链接才能输出⽬标代
码,然后由机器执⾏⽬标代码。⽬标代码是有机器指令组成,不能独⽴运⾏,因为源程序中可能使⽤了⼀些汇编程序不能解释引⽤的库函数,⽽库函数⼜不在源程序中,此时还需要链接程序完成外部引⽤和⽬标模板调⽤的链接任务,最后才能输出可执⾏代码。
2. 解释型语⾔,解释器不产⽣⽬标机器代码,⽽是产⽣中间代码,这种中间代码与机器代码不同,中
间代码的解释是由软件⽀持的,不
能直接使⽤在硬件上。该软件解释器通常会导致执⾏效率较低,⽤解释型语⾔编写的程序是由另⼀个可以理解中间代码的解释程序执⾏的。和编译的程序不同的是, 解释程序的任务是逐⼀将源代码的语句解释成可执⾏的机器指令,不需要将源程序翻译成⽬标代码再执⾏。对于解释型语⾔,需要⼀个专门的解释器来执⾏该程序,每条语句只有在执⾏是才能被翻译,这种解释型语⾔每执⾏⼀次就翻译⼀次,因⽽效率低下。
3. Java解释器,java很特殊,java是需要编译的,但是没有直接编译成机器语⾔,⽽是编译成字节码,然后在Java虚拟机上⽤解释的⽅
式执⾏字节码。Python也使⽤了类似的⽅式,先将python编译成python字节码,然后由⼀个专门的python字节码解释器负责解释执⾏字节码。
4. python是⼀门解释语⾔,但是出于效率的考虑,提供了⼀种编译的⽅法。编译之后就得到pyc⽂件,存储了字节码。python这点和
java很类似,但是java与python不同的是,python是⼀个解释型的语⾔,所以编译字节码不是⼀个强制的操作,事实上,编译是⼀个⾃动的过程,⼀般不会在意它的存在。编译成字节码可以节省加载模块的时间,提⾼效率。
5. 除了效率之外,字节码的形式也增加了反向⼯程的难度,可以保护源代码。这个只是⼀定程度上的保护,反编译还是可以的。(2)
⼀、编译过程概述
当我们执⾏Python代码的时候,在Python解释器⽤四个过程“拆解”我们的代码,最终被CPU执⾏返回给⽤户。
⾸先当⽤户键⼊代码交给Python处理的时候会先进⾏词法分析,例如⽤户键⼊关键字或者当输⼊关键字有误时,都会被词法分析所触发,不正确的代码将不会被执⾏。
下⼀步Python会进⾏语法分析,例如当"for i in test:"中,test后⾯的冒号如果被写为其他符号,代码依旧不会被执⾏。
下⾯进⼊最关键的过程,在执⾏Python前,Python会⽣成.pyc⽂件,这个⽂件就是字节码,如果我们不⼩⼼修改了字节码,Python 下次重新编译该程序时会和其上次⽣成的字节码⽂件进⾏⽐较,如果不匹配则会将被修改过的字节码⽂件进⾏覆盖,以确保每次编译后字节码的准确性。
那么什么是字节码?字节码在Python虚拟机程序⾥对应的是PyCodeObject对象。.pyc⽂件是字节码在磁盘上的表现形式。简单来说就是在编译代码的过程中,⾸先会将代码中的函数、类等对象分类处
理,然后⽣成字节码⽂件。有了字节码⽂件,CPU可以直接识别字节码⽂件进⾏处理,接着Python就可执⾏了。
⼆、过程图解
三、编译字节码
Python中有⼀个内置函数compile(),可以将源⽂件编译成codeobject,⾸先看这个函数的说明:
compile(...) compile(source, filename, mode[, flags[, dont_inherit]]) -> code object
参数1:源⽂件的内容字符串
参数2:源⽂件名称
参数3:exec-编译module,single-编译⼀个声明,eval-编译⼀个表达式 ⼀般使⽤前三个参数就够了
使⽤⽰例:
1 2 3 4 5 6 7 8 9 10 11 12 13#src_file.py
#some function
def f(d=0):
c=1
print"hello"
a=9
b=8
f()
>>> a=open('src_file.py','r').read() #命令⾏模式中打开源⽂件进⾏编译>>> co=compile(a,'src_file','exec')
>>> type(co)
<type'code'> #编译出了codeobject对象
四、codeobject对象的属性
codeobject有哪些变量,接上节的内容分析⼀下:
1 2 3>>> _names #所有的符号名称('f', 'a', 'b')
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23>>> _name #模块名、函数名、类名
<module>
>>> _consts #常量集合、函数f和两个int常量a,b,d
(0, <code object f at 0xb7273b18, file"src_file", line 2>, 9, 8, None)
>>> _consts[1].co_varnames #可以看到f函数也是⼀个codeobject,打印f中的局部变量('c',)
>>> _code #字节码指令
dZdZdZedS
>>> _consts[1].co_firstlineno #代码块在⽂件中的起始⾏号
2
>>> _stacksize #代码栈⼤⼩
2
>>> _filename #⽂件名
src_file #模块名、函数名、类名
codeobject的co_code代表了字节码,这个字节码有什么含义?我们可以使⽤dis模块进⾏python的反编译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18import dis
dis.dis(co)
>>> output
20LOAD_CONST 0(0)
3LOAD_CONST 1(<code object f at 0xb7273b18, file"src_file", line 2>) 6MAKE_FUNCTION 1
9STORE_NAME 0(f)
512LOAD_CONST 2(9)
15STORE_NAME 1(a)
618LOAD_CONST 3(8)
21STORE_NAME 2(b)
724LOAD_NAME 0(f)
27CALL_FUNCTION 0
30POP_TOP
31LOAD_CONST 4(None)
34RETURN_VALUE
从反编译的结果来看,python字节码其实是模仿的x86的汇编,将代码编译成⼀条⼀条的指令交给⼀个虚拟的cpu去执⾏。
第⼀列:⾏号
第⼆列:指令在代码块中的偏移量
第三列:指令
第四列:操作数
第五列:操作数说明
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论