Python的编译和执行


一、编译过程

当我们执行Python代码的时候,Python解释器用四个过程“拆解”我们的代码,最终被CPU执行返回给用户。

  1. 词法分析:例如用户键入关键字或者当输入关键字有误时,都会被词法分析所触发,不正确的代码将不会被执行。

  2. 语法分析:例如当"for i in test:"中,test后面的冒号如果被写为其他符号,代码依旧不会被执行。

  3. 编译:生成.pyc文件(字节码),简单来说就是在编译代码的过程中,首先会将代码中的函数、类等对象分类处理,然后生成字节码文件。字节码在Python虚拟机程序里对应的是PyCodeObject对象。.pyc文件是字节码在磁盘上的表现形式。

  4. 执行:有了字节码文件,CPU可以直接识别字节码文件进行处理,接着Python就可执行了。

在这里插入图片描述
Python中有一个内置函数compile(),可以将源文件编译成codeobject,首先看这个函数的说明:

compile(…) compile(source, filename, mode[, flags[, dont_inherit]]) -> code object

参数1:源文件的内容字符串

参数2:源文件名称

参数3:exec-编译module,single-编译一个声明,eval-编译一个表达式 一般使用前三个参数就够了

使用示例:

#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对象


>>> print co.co_names    #所有的符号名称

('f', 'a', 'b')

>>> print co.co_name    #模块名、函数名、类名

<module>

>>> print co.co_consts    #常量集合、函数f和两个int常量a,b,d

(0, <code object f at 0xb7273b18, file "src_file", line 2>, 9, 8, None)

>>> print co.co_consts[1].co_varnames    #可以看到f函数也是一个codeobject,打印f中的局部变量

('c',)

>>> print co.co_code    #字节码指令

dZdZdZedS

>>> print co.co_consts[1].co_firstlineno    #代码块在文件中的起始行号

2

>>> print co.co_stacksize    #代码栈大小

2

>>> print co.co_filename    #文件名

src_file    #模块名、函数名、类名

使用dis模块进行python的反编译:

import dis

dis.dis(co)

>>> output

 2        0 LOAD_CONST               0 (0)

          3 LOAD_CONST               1 (<code object f at 0xb7273b18, file "src_file", line 2>)

          6 MAKE_FUNCTION            1

          9 STORE_NAME               0 (f)

 5        12 LOAD_CONST              2 (9)

          15 STORE_NAME              1 (a)

 

 6        18 LOAD_CONST              3 (8)

          21 STORE_NAME              2 (b)

 

 7        24 LOAD_NAME               0 (f)

          27 CALL_FUNCTION           0

          30 POP_TOP            

          31 LOAD_CONST              4 (None)

          34 RETURN_VALUE

从反编译的结果来看,python字节码其实是模仿的x86的汇编,将代码编译成一条一条的指令交给一个虚拟的cpu去执行。

第一列:行号
第二列:指令在代码块中的偏移量
第三列:指令
第四列:操作数
第五列:操作数说明

二、执行原理

我们都知道,使用C/C++之类的编译性语言编写的程序,需要从源文件转换成计算机使用的机器语言,经过链接器链接之后形成了二进制的可执行文件。运行该程序的时候,就可以把二进制程序从硬盘载入到内存中并运行。

但是对于Python而言,Python源码不需要编译成二进制代码,它可以直接从源代码运行程序。当我们运行Python文件程序的时候,Python解释器将源代码转换为字节码,然后再由Python解释器来执行这些字节码。这样,Python就不用担心程序的编译库的链接加载等问题了。

对于Python解释语言,有以下3方面的特性:

  1. 每次运行都要进行转换成字节码,然后再有虚拟机把字节码转换成机器语言,最后才能在硬件上运行。与编译性语言相比,每次多出了编译和链接的过程,性能肯定会受到影响;而Python并不是每次都需要转换字节码,解释器在转换之前会判断代码文件的修改时间是否与上一次转换后的字节码pyc文件的修改时间一致,若不一致才会重新转换。

  2. 由于不用关心程序的编译和库的链接等问题,开发的工作更加轻松。

  3. Python代码与机器底层更远了,Python程序更加易于移植,基本上无需改动就能在多平台上运行。

具体计算机上实现一种语言,首先要确定的是表示该语言语义解释的虚拟计算机,一个关键的问题是程序执行时的基本表示是实际计算机上的机器语言还是虚拟机的机器语言,这个问题决定了语言的实现。根据这个问题的回答,可以将程序设计语言划分为两大类:编译型语言和解释型语言。

  • 编译实现的语言,如:C、C++、Fortran、Pascal、Ada。由编译型语言编写的源程序需要经过编译,汇编和链接才能输出目标代码,然后由机器执行目标代码。目标代码是有机器指令组成,不能独立运行,因为源程序中可能使用了一些汇编程序不能解释引用的库函数,而库函数又不在源程序中,此时还需要链接程序完成外部引用和目标模板调用的链接任务,最后才能输出可执行代码。

  • 解释型语言,解释器不产生目标机器代码,而是产生中间代码,这种中间代码与机器代码不同,中间代码的解释是由软件支持的,不能直接使用在硬件上。该软件解释器通常会导致执行效率较低,用解释型语言编写的程序是由另一个可以理解中间代码的解释程序执行的。和编译的程序不同的是,解释程序的任务是逐一将源代码的语句解释成可执行的机器指令,不需要将源程序翻译成目标代码再执行。对于解释型语言,需要一个专门的解释器来执行该程序,每条语句只有在执行是才能被翻译,这种解释型语言每执行一次就翻译一次,因而效率低下。

Java是需要编译的,但是没有直接编译成机器语言,而是编译成字节码,然后在Java虚拟机上用解释的方式执行字节码。Python也使用了类似的方式,先编译成python字节码,然后由一个专门的python字节码解释器负责解释执行字节码。但是出于效率的考虑,提供了一种编译的方法。编译之后就得到pyc文件,存储了字节码。python这点和java很类似,但是java与python不同的是,python是一个解释型的语言,所以编译字节码不是一个强制的操作,事实上,编译是一个自动的过程,一般不会在意它的存在。编译成字节码可以节省加载模块的时间,提高效率。而且字节码的形式也增加了反向工程的难度,可以保护源代码。这个只是一定程度上的保护,反编译还是可以的。

Python调用DLL动态链接库:
https://www.cnblogs.com/FHC1994/p/11421229.html


以上内容来源于网络知识总结,如有侵权请私信联系立即删除:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值