Python学习系列之pyc文件

一、什么是pyc文件

pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code字节码,py文件变成pyc文件后,加载的速度有所提高,而且pyc是一种跨平台的字节码,是由Python的虚拟机来执行的,这个是类似于Java或者.NET的虚拟机的概念,从而实现跨平台。pyc文件经过python解释器最终会生成机器码运行。所以pyc文件是可以跨平台部署的,类似Java的.class文件。如果py文件改变,也会重新生成pyc文件。

pyc的内容,是跟python的版本相关的,不同版本编译后的pyc文件是不同的,2.5编译的pyc文件,2.4版本的python是无法执行的。

Python是完全面向对象的语言,Python文件在经过解释器解释后生成字节码对象PyCodeObject,pyc文件可以理解为是PyCodeObject对象的持久化保存方式。pyo是优化的字节码文件。当python程序运行时,编译的结果是保存在位于内存中的PyCodeObject中,当Python程序运行结束时,Python解释器则将PyCodeObject写回到pyc文件中。当python程序第二次运行时,首先程序会在硬盘中寻找pyc文件,如果找到,则直接载入,否则就重复上面的过程。所以,我们可以说pyc文件其实是PyCodeObject的一种持久化保存方式。

Python选项中:

  • -O,表示优化生成.pyo字节码。
  • -OO,表示进一步移除-O选项生成的字节码文件中的文档字符串(这是在作用效果上解释的,而不是说从-O选项得到的文件去除)。
  • -m,表示导入并运行指定的模块,也就是生成pyc文件。

其他文件:

  • pyo是优化编译后的程序 python -O 源文件即可将源程序编译为pyo文件。相比于.pyc文件更小,也可以提高加载速度。但对于嵌入式系统,它可将所需模块编译成.pyo文件以减少容量
  • pyd是python的动态链接库。

1.1 为什么需要pyc文件

(1)python为了提高运行效率也会进行编译

python是解释型语言,运行的时候需要通过python解释器解释执行。我们知道解释型语言最大的问题是运行速度比编译性慢,因为编译性语言会产生二进制执行文件,也就是不跨平台的机器指令。所以如果先编译出pyc文件后,可以节省编译这一步的耗时时间。在一定程度上提高程序的运行速度。

我们一般认为C/C++是编译语言,ruby是解释型语言。而Java是先编译后解释的语言:Java首先是通过编译器编译成字节码文件,然后在运行时通过解释器给解释成机器文件。Java,.net,python等基于虚拟机的语言,我们又不能把语言纯粹地分成解释型和编译型这两种。Python也是一门基于虚拟机的语言。当我们在命令行中输入python hello.py时,其实是激活了Python的“解释器”,告诉“解释器”:你要开始工作了。可是在“解释”之前,其实执行的第一项工作和Java一样,是编译。

一般的IDE,比如Eclipse都会将这两步合并为一步。先编译后解释:

Python解释器将源码转换为字节码(跨平台,统一的,只不过不同OS的虚拟机基于它产生不同的机器指令实现跨平台),然后再由解释器来执行这些字节码,转化为合适的机器指令,然后执行。

(2)防止源码泄露。

因为py文件是可以直接看到源码的,如果你是开发商业软件的话,不可能把源码也泄漏出去吧?所以就需要编译为pyc后,再发布出去。当然,pyc文件也是可以反编译的,不同版本编译后的pyc文件是不同的,根据python源码中提供的opcode,可以根据pyc文件反编译出py文件源码,网上可以找到一个反编译python2.3版本的pyc文件的工具,不过该工具从python2.4开始就要收费了,如果需要反编译出新版本的pyc文件的话,就需要自己动手了,不过你可以自己修改python的源代码中的opcode文件,重新编译python,从而防止不法分子的破解。

1.2 一定产生pyc吗?

实际开发中我们发现有的py文件产生pyc,而有的py文件不产生pyc。

pyc文件只有在文件被当成模块导入时才会生成。也就是说,Python解释器认为,只有import进行的模块才需要被重用。 生成pyc文件的好处显而易见,当我们多次运行程序时,不需要重新对该模块进行重新的解释。主文件一般只需要加载一次不会被其他模块导入,所以一般主文件不会生成pyc文件。

当有别的程序再次import这些模块时,python就不用再重新解析py文件,而是读入pyc文件就可以了。

1.3 如何产生pyc文件?

因为并不是一定会产生pyc文件,所以在什么情况下产生pyc文件?如何产生?

(1)被当做模块调用的时候会自动生成pyc文件

  一般是python脚本被当做模块,被其他脚本文件调用时,就会产生pyc文件,举个例子:

  如果你写了一个脚本文件image.py是用来生成验证码的,如果你在index.py文件中想引用这个脚本的功能,就需要通过import导入image.py文件,然后,如果你运行index.py文件,那就会在image.py文件所在的路径生成一个image.pyc文件。

(2)对于py文件,可以执行下面命令来生成pyc文件。参数-m。

python中.pyc文件是什么?

(3)通过代码来生成pyc文件。

生成单个pyc文件

Python提供了内置的类库来实现把py文件编译为pyc文件,这个模块就是 py_compile 模块。

python中.pyc文件是什么?

compile函数原型:

compile(file[, cfile[, dfile[, doraise]]])
  • file 表示需要编译的py文件的路径
  • cfile 表示编译后的pyc文件名称和路径,默认为直接在file文件名后加c 或者 o,o表示优化的字节码
  • dfile it is used as the name of the source file in error messages instead of file
  • doraise 可以是两个值,True或者False,如果为True,则会引发一个PyCompileError,否则如果编译文件出错,则会有一个错误,默认显示在sys.stderr中,而不会引发异常

批量生成pyc文件

一般来说,我们的工程都是在一个目录下的,一般不会说仅仅编译一个py文件而已,而是需要把整个文件夹下的py文件都编译为pyc文件,python又为了我们提供了另一个模块:compileall 。使用方法如下:

import compileall

compileall.compile_dir(r'H:/game')

这样就把game目录,以及其子目录下的py文件编译为pyc文件了。来看下compile_dir函数的说明:

compile_dir(dir[, maxlevels[, ddir[, force[, rx[, quiet]]]]])
  • dir 表示需要编译的文件夹位置
  • maxlevels 表示需要递归编译的子目录的层数,默认是10层,即默认会把10层子目录中的py文件编译为pyc
  • ddir it is used as the base path from which the filenames used in error messages will be generated。
  • force 如果为True,则会强制编译为pyc,即使现在的pyc文件是最新的,还会强制编译一次,pyc文件中包含有时间戳,python编译器会根据时间来决定,是否需要重新生成一次pyc文件
  • rx 表示一个正则表达式,比如可以排除掉不想要的目录,或者只有符合条件的目录才进行编译
  • quiet 如果为True,则编译后,不会在标准输出中,打印出信息

1.4 pyc文件的过期时间

  • 在生成pyc文件的同时,写入了一个Long型的变量,用于记录最近修改的时间;
  • 每次载入之前都先检查一下py文件和pyc文件的最后修改日期,如果不一致则会生成一个新的pyc文件

1.5 pyc文件的组成

pyc文件一般由3个部分组成:

  • 最开始4个字节是一个Maigc int,标识此pyc的版本信息,不同的版本的 Magic 都在 Python/import.c 内定义
  • 接下来四个字节还是个int,是pyc产生的时间(1970.01.01到产生pyc时候的秒数)
  • 接下来是个序列化了的 PyCodeObject(此结构在 Include/code.h 内定义),序列化方法在 Python/marshal.c 内定义

1.6 导入和模块

模块其实也是一个python文件。我们把一些功能相关的代码写到一个模块里。这样,当你需要用到某个功能时,就将这个模块导入,就可以直接使用它的函数了,非常的方便。

在python中,一个模块就是一个py文件,可以说一个文件就是一个独立的模块,一个独立的模块就是一个py文件。你自己写的py文件,可以引入别人的模块;别人也可以引用你的这个py文件,去使用你写的函数 。所以,模块中可以有函数,也可以有类

导入一个模块,使用import。

1.7 pyo文件

python -O -m py_compile file.py

可以编译成pyo文件,是优化过的字节码文件。
1. 其中的 -m 相当于脚本中的import,这里的-m py_compile 相当于上面的 import py_compile 
2. -O 如果改成 -OO 则是删除相应的 pyo文件,具体帮助可以在控制台输入 python -h 查看

总结

  • Python的pyc文件从一定意义上可以减少解释器解释Python文件的时间,不必每次都要解释编译一遍,方便下次快速加载,有点类似数据库的缓存吧;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章