pyc文件是py文件编译后生成的字节码文件(byte code)。pyc文件经过python解释器最终会生成机器码运行。在不想暴露源码的情况下,可以将.py文件生成.pyc文件。 那么python如何生成pyc呢?一起来了解下吧:
python如何生成pyc
生成单个pyc文件:
命令:
python -m py_compile file.py
python -m py_compile /root/src/{file1,file2}.py
脚本:
import py_compile
py_compile.compile('path') //path是包括.py文件名的路径
批量生成pyc文件:
命令:
python -m compileall /root/src/
脚本:
import compileall
compileall.compile_dir(r'H:game')
PS:生成pyc文件后删除原py文件,直接执行pyc文件(方法同py文件)
python怎么将py生成pyc文件
生成单个pyc文件
python就是个好东西,它提供了内置的类库来实现把py文件编译为pyc文件,这个模块就是 py_compile 模块。
使用方法非常简单,如下所示,直接在idle中,就可以把一个py文件编译为pyc文件了。(假设在windows环境下)
import py_compile
py_compile.compile(r'H:/game/test.py')
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中,而不会引发异常
(来自python2.5文档)
批量生成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,则编译后,不会在标准输出中,打印出信息。
代码片段:
#-*- coding=utf-8
'''
#编译目录下所有py文件为 pyc文件
import compileall
compileall.compile_dir(r"d:python") #r"d:python" 路径
'''
#编译 单个py文件为 pyc文件
import py_compile
py_compile.compile(r"d:pythontest.py") # r"d:pythontest.py":路径
python生成pyc文件的方法
什么是pyc文件
pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code,py文件变成pyc文件后,加载的速度有所提高,而且pyc是一种跨平台的字节码,是由python的虚拟机来执行的,这个是类似于Java或者.NET的虚拟机的概念。pyc的内容,是跟python的版本相关的,不同版本编译后的pyc文件是不同的,2.5编译的pyc文件,2.4版本的 python是无法执行的。
什么是pyo文件
pyo是优化编译后的程序 python -O 源文件即可将源程序编译为pyo文件
什么是pyd文件
pyd是python的动态链接库。
为什么需要pyc文件
这个需求太明显了,因为py文件是可以直接看到源码的,如果你是开发商业软件的话,不可能把源码也泄漏出去吧?所以就需要编译为pyc后,再发布出去。当然,pyc文件也是可以反编译的,不同版本编译后的pyc文件是不同的,根据python源码中提供的opcode,可以根据pyc文件反编译出 py文件源码,网上可以找到一个反编译python2.3版本的pyc文件的工具,不过该工具从python2.4开始就要收费了,如果需要反编译出新版本的pyc文件的话,就需要自己动手了(俺暂时还没这能力^--^),不过你可以自己修改python的源代码中的opcode文件,重新编译 python,从而防止不法分子的破解。
生成单个pyc文件
python就是个好东西,它提供了内置的类库来实现把py文件编译为pyc文件,这个模块就是 py_compile 模块。
使用方法非常简单,如下所示,直接在idle中,就可以把一个py文件编译为pyc文件了。(假设在windows环境下)
import py_compile
py_compile.compile(r'H:gametest.py')
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中,而不会引发异常
(来自python2.5文档)
批量生成pyc文件
一般来说,我们的工程都是在一个目录下的,一般不会说仅仅编译一个py文件而已,而是需要把整个文件夹下的py文件都编译为pyc文件,python又为了我们提供了另一个模块:compileall 。使用方法如下:
import compileall
compileall.compile_dir(r'H:game')
也可以直接用命令行编译一个目录下的文件,如:# python -m compileall /root/src/
这样就把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,则编译后,不会在标准输出中,打印出信息
(来自python2.5文档)
通过上面的方法,可以方便的把py文件编译为pyc文件了,从而可以实现部分的源码隐藏,保证了python做商业化软件时,保证了部分的安全性吧,继续学习下,看怎么修改opcode。
Python什么情况下会生成pyc文件
作为Python爱好者,需要了解.py脚本的基本运行机制及特性:
在很多工作上Python的运行流程基本上取决于用户,因此源码不需要编译成二进制代码(否则无法实现大部分贴近用户的特性),而直接从源码运行程序。当我们运行python文件程序的时候,Python解释器将源码转换为字节码,然后再由解释器来执行这些字节码。因此总的来说,它具有以下三条特性
源码距离底层更远(根据官方文档的解释。不说,你们也感觉得到)(。?`ω??)
运行时都需要生成字节码,交由虚拟机执行。(你们问我虚拟机在哪儿?!你们也不看看各自都是用什么软件执行的!没错,就是解释器,别和我说是IDLE啊。虚拟机具体实现了由switch-case语句构成的框架函数PyEval_EvalFrameEx,刚刚说的字节码就是这货执行的)
每次执行脚本,虚拟机总要多出加载和链接的流程。(所以呢,相比于编译型语言就有点慢了。这与“有丝分裂间期”一样,准备东西也要花时间啊!)
那么,有人要问了:“不是说,运行时总要生成字节码么!那,字节码都去哪儿了?”
咳咳,别急!容我先说说,虚拟机它是怎么执行脚本的(咕噜咕噜喝杯水...):
完成模块的加载和链接;
将源代码翻译为PyCodeObject对象(这货就是字节码),并将其写入内存当中(方便CPU读取,起到加速程序运行的作用);
从上述内存空间中读取指令并执行;
程序结束后,根据命令行调用情况(即运行程序的方式)决定是否将PyCodeObject写回硬盘当中(也就是直接复制到.pyc或.pyo文件中);
之后若再次执行该脚本,则先检查本地是否有上述字节码文件。有则执行,否则重复上述步骤。
你看!在我们点击(或输入命令)运行脚本,并悠闲地喝咖啡时,“人家”虚拟机做了这么多的事情。不过,你有没有发现.pyc或.pyo文件是否生成,是取决于我们如何运行程序的(虽然我们不知道要怎么做(? ??_??)? )。
同样,有人会吐槽:“哼!为什么不直接生成这些文件,这样来得不是‘更快、更高、更强’!”
其实,虚拟机也是讲究效率的。毕竟对于比较大的项目,要将PyCodeObject写回硬盘也是不可避免地要花些时间的,而且它又不知道你是不是也就只执行一次,之后就对刚刚跑完的脚本“弃之不顾”了呢。不过,它其实也有贴心的一面。比如,
若你在命令行直接输入“python path/to/projectDir”(假设projectDir目录含有“__main__.py”文件,以及其他将要调用的模块),那么程序运行结束后便自动为当前目录下所有的脚本生成字节码文件,并保存于本地新文件夹__pycache__当中。(这也有可能是IDE写小项目时自动生成.pyc文件的原因,不过问题描述略微暧昧。详情参见上面知乎问题板块)
或者是,在命令行输入“python path/to/projectDir/__main__.py”,则生成除__main__.py外脚本的字节码文件。不过总的来说,上述这两种行为都大大缩短了项目运行前的准备时间(毕竟分工明确的程序,规模应该不会太小,复用率也不会太低。除非吃饱了撑着,搞出这么多事情(Θ皿Θメ))
模块在每次导入前总会检查其字节码文件的修改时间是否与自身的一致。若是则直接从该字节码文件读取内容,否则源模块重新导入,并在*生成同名文件覆盖当前已有的字节码,从而完成内容的更新(详见import.py)。这样,就避免了修改源代码后与本地字节码文件产生冲突(当然,设计者也不会这么傻。╮( ̄▽ ̄")╭)。
若想优化生成字节码,应注意这两点:
.pyc文件是由.py文件经过编译后生成的字节码文件,其加载速度相对于之前的.py文件有所提高,而且还可以实现源码隐藏,以及一定程度上的反编译。比如,Python3.3编译生成的.pyc文件,Python3.4就别想着去运行啦!→_→
.pyo文件也是优化(注意这两个字,便于后续的理解)编译后的程序(相比于.pyc文件更小),也可以提高加载速度。但对于嵌入式系统,它可将所需模块编译成.pyo文件以减少容量。
但总的来说,作用上是几乎与原来的.py脚本没有区别的,也就是“然并卵 ”(当然,并非毫无作用。比如,我个人觉得用处*的地方就是防止别人偷看我的代码。_(:з」∠)_毕竟.py源文件是直接以源码的形式呈现给大家的)。╮(╯▽╰)╭ 呃...这么说,好像又有点自相矛盾的赶脚。
在所有的Python选项中:
-O,表示优化生成.pyo字节码(这里又有“优化”两个字,得注意啦!)
-OO,表示进一步移除-O选项生成的字节码文件中的文档字符串(这是在作用效果上解释的,而不是说从-O选项得到的文件去除)
-m,表示导入并运行指定的模块
对此,我们可以使用如下格式运行.py文件来生成.pyc文件(以下调用均假设/path/to目录含有.py脚本):
python -m py_compile /path/to/需要生成.pyc的脚本.py #若批量处理.py文件 #则替换为/path/to/{需要生成.pyc的脚本1,脚本2,...}.py #或者/path/to/
其效果等效于如下代码:
import py_compile py_compile.compile(r'/path/to/需要生成.pyc的脚本.py') #同样也可以是包含.py文件的目录路径 #此处尽可能使用raw字符串,从而避免转义的麻烦。比如,这里不加“r”的话,你就得对斜杠进行转义
py_compile是Python的自带模块,这里面就两个函数(看到这个,我笑了(??? ? ???)噗噗
)。其下的py_compile.compile(file[, cfile[, dfile[, doraise]]])可将.py文件编译生成.pyc文件(默认),对应的参数解释如下
file,表示需要生成.pyc或.pyo文件的源脚本名(字符串);
cfile,表示需要生成.pyc或.pyo文件的目标脚本名。呃...好像没有区别(>﹏<) ,也就是源脚本-----[巴拉拉赐予你力量!编译!]( * ̄▽ ̄)o ─═≡※:☆----->目标脚本。当然,它默认是以.pyc为扩展名的路径名的字符串(呼...好长)。此外,当且仅当所使用的解释器允许编译成.pyo文件,才能以“.pyo”结尾。这也就是我上面为什么会在函数功能解释上加上“(默认)”这两个字的原因。
dfile,表示编译出错时,将报错信息中的名字“file”替换为“dfile”。
doraise,设置是否忽略异常。若为True,则抛出PyCompileError异常;否则直接将错误信息写入sys.stderr(什么!不知道sys.stderr?!温馨提示:sys.stderr是Python自带的标准错误输出)
(╯' - ')╯︵ ┻━┻ (掀桌子) ┬─┬ ノ( ' - 'ノ) (摆好摆好) (╯°Д°)╯︵ ┻━┻(再TA喵掀一次)
另外,生成.pyo文件的格式调用如下:
python -O -m py_compile /path/to/需要生成.pyo的脚本.py
那么,有人要问了:为什么不是像生成.pyc文件那样采用“python -O /path/to/需要生成.pyo的脚本.py”形式的调用? “忘记”说明这一点了,很多博客以及书籍都像我上面那样解释“-O”选项的作用,但详细来解释的话是 -O选项,将.pyc文件优化(注意我一直强调的“优化”二字,这里就用到啦!)为.pyo文件,而不是将.py文件优化编译为.pyo文件。(其直接的结果是优化编译后的文件略微小于.pyc文件,也就是“减肥”了。现在,大家知道.pyo文件为什么小的原因了吧!)
注意: 以上无论是生成.pyc还是.pyo文件,都将在当前脚本的目录下生成一个含有字节码的文件夹__pycache__。
可能还有人会问,.pyd文件又是什么鬼(>﹏<)?!(问题真多,精分ing...) 别在意,那只是Python的动态链接库。如果要深究,还得扯上C++的知识(长篇大论的,会被喷的啊)。
再啰嗦一句:生成字节码的方法多了去了,不止以上这几种。比如,你们不妨试试将上面命令行调用中的“py_compile”改成“compileall”,而代码行中的“py_compile.compile”改成“compileall.compile_file”或“compileall.compile_dir”,又或者直接使用带有编译功能的IDE生成字节码。
更多培训课程,学习资讯,课程优惠,课程开班,学校地址等学校信息,请进入 天才领路者网站详细了解
咨询电话:400-850-8622