目前做一个项目,为了保护源码,如何发布解析之后的二进制文件.pyc,给工程使用,这里做一个较为简单和详细的解释。
包:一个文件夹,用来存放模块和子包。包里一般会有一个__init__.py 的文件(也可以没有),包里一般也会有一个__pycache__文件夹,存放 .py 文件经解释器解释后的中间字节码(二进制文件)。
模块: 可以作为模块的文件有 .py ,.pyc, .pyo, .pyd, .so, .dll文件。这里主要介绍 .pyc 文件。
python 解释器在解释.py 文件时,会优先查看是否有对应的最新的.pyc 文件,有的话会直接加载.pyc 文件。在加载之前,会检查.pyc 文件是不是最新的。
pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code,py文件变成pyc文件后,加载的速度有所提高,而且pyc是一种跨平台的字节码,是由python的虚拟机来执行的,pyc的内容,是跟python的版本相关的,不同版本编译后的pyc文件是不同的,2.5编译的pyc文件,2.4版本的python是无法执行的。
为什么需要pyc
因为py文件是可以直接看到源码的,如果你是开发商业软件的话,不可能把源码也泄漏出去吧?所以就需要编译为pyc后,再发布出去。当然,pyc文件也是可以反编译的,不同版本编译后的pyc文件是不同的,根据python源码中提供的opcode,可以根据pyc文件反编译出py文件源码,这一步不重要。
代码编译生成
对于单个文件,使用方法非常简单,如下所示,***.py是需要编译的python源文件:
import py_compile
py_compile.compile('****.py')
对于多个文件一般来说,我们的工程都是在一个目录下的,一般不会说仅仅编译一个py文件而已,而是需要把整个文件夹下的py文件都编译为pyc文件,python又为了我们提供了另一个模块:compileall 。使用方法如下:
import compileall
compileall.compile_dir(r'/Users/***/PythonFiles/')
Python Shell 命令生成
直接通过命令来运行,可以看到下面的命令中并没有用到compile()函数, 这是因为py_compile模块的main()函数中调用了compile().
python3
-
m py_compile ****.py
python3
-
O
-
m py_compile ****.py
-O 优化成字节码
-m 表示把后面的模块当成脚本运行
-OO 表示优化的同时删除文档字符串
如果你想看compile(), compile_dir(), compile_path()具体每个参数是干吗用的,可以使用print py_compile.compile().__doc__来查看,或者直接打开py_compile.py,compileall.py文件来看。
直接使用python编译源码时,会生成对应的(.pyc)文件,这些文件会被存放在__pycache__文件夹下,我们可以将里边的文件copy出来,这时候可以删除对应的.py文件,同时需要将***.pyc文件的名字进行修改。
例如: python 的.py文件叫 main.py, 生成的.pyc文件叫main.cpython-36.pyc,在我们使用该模块的时候,需要将main.cpython-36.pyc修改为main.pyc, 这里的“cpython-36”表示的是我在python 3.6 的环境下编译的。
注意 :如果你直接从一个环境中copy到另一个环境中会出现一些bug,我遇到一个,在这里分享一下,如果有其它有趣的bug,记得留言分享。
ImportError: bad magic number in 'main':b '3\r*****'
主要原因是编译的python版本与目前运行的python版本不同导致的。编译成.pyc文件之后,尽量保证编译环境与运行环境一致。