赞
踩
提示:编译过程中所有文件路径最好不要出现中文
pyc 是由 py 文件经过编译后生成的二进制字节码(byte code)文件;
pyc 文件的加载速度比 py 文件快;
pyc 文件是一种跨平台的字节码,由 python 的虚拟机来执行;
pyc 文件的内容跟 python 版本相关,不同的 python 版本编译生成不同的 pyc 文件,只能在相同版本环境下执行;
.pyc 文件结构介绍参考:https://www.iteye.com/topic/382423、https://yq.aliyun.com/articles/599833#
.pyc 文件的前 8 个字节含义:
- 四个字节的 magic number
- 四个字节的 timestamp
头四个是 magic number 很多 pyc 都在这个上面做文章,这修改成不合法的,然后你反编译就是败了,你可以找你自己编译成功的 pyc 头直接覆盖掉他的头 8 个字节就可以了, timestamp 是文件的修改时间,主要是当源码有改变的时候 python 就可以重新生成 pyc 文件.
利用compileall
和py_compile
来预编译 python 代码:
这两个从某种意义上是互通的,python 预装了这两个东西,
python -m compileall test.py #把单个.py文件编译为字节码文件
python -m compileall /path/src/ #批量生成字节码文件,/path/src/是包含.py文件名的路径
python -m py_compile test.py #把单个.py文件编译为字节码文件
python -m py_compile /path/src/ #批量生成字节码文件,仅将/path/src/的下一层.py文件编译,不会递归执行
上面的 py_compile 针对文件夹是会有一些问题,但理论上这种语法应该是可以的。
可根据项目需要写成编译脚本:
对于 compileall 更详细的参数以及命令解析可以参考的链接:https://docs.python.org/3/library/compileall.html
import compileall # compileall.compile_file编译单个文件; compileall.compile_file('main.py') #compile_dir 函数编译文件夹下的py文件 compileall.compile_dir('Lib/', force=True) # 使用多处理器编译 compileall.compile_dir('Lib/', workers=2) # Perform same compilation, excluding files in .svn directories. import re compileall.compile_dir('Lib/', rx=re.compile(r'[/\\][.]svn'), force=True) # pathlib.Path objects can also be used. import pathlib compileall.compile_dir(pathlib.Path('Lib/'), force=True)
函数原型:
py_compile.compile(file, cfile=None, dfile=None, doraise=False, optimize=-1, invalidation_mode=None):
True
,编译发生错误时则会引发一个 PyCompileError; 如果为False
, 编译文件出错时,则会有输出一个错误信息,而不会引发异常import py_compile
py_compile.compile(r'Downloads/md5.py')
'Downloads/__pycache__/md5.cpython-37.pyc'
与 py 文件一样使用,最好将文件名中间类似 cpython-36 的部分去掉,否则可能出现导包错误 ModuleNotFoundError:
源代码文件经过优化编译后生成的文件,无法用文本编辑器进行编辑
Python3.5 之后,不再使用.pyo 文件名,而是使用类似“xxx.opt-n.pyc 的文件名;
编译成 pyc 和 pyo 本质上和 py 没有太大区别,只是对于这个模块的加载速度提高了,并没有提高代码的执行速度。
pyo 文件其实很简单,就是上面 pyc 命令的改版:
python -O -m py_compile file.py
python -O -m py_compile /path/src/
python -O -m compileall file.py
python -O -m compileall /path/src/
或者
python -OO -m py_compile file.py
python -OO -m py_compile /path/src/
python -OO -m compileall file.py
python -OO -m compileall /path/src/
与 py 文件一样运行、导入
python sample.cpython-36.pyo
from sample import *
pyd 格式是 D 语言(C/C++综合进化版本)生成的二进制文件,是 python 的动态链接库;
参考信息:https://docs.python.org/3/faq/windows.html?highlight=pyd#is-a-pyd-file-the-same-as-a-dll
windows 编译环境鄙人用的 Visual Studio 2019
利用 Cython 模块,根据编译环境不同生成不同文件。
pip intall Cython
注意:程序所在的目录路径不能包含中文文字
# 脚本文件
from distutils.core import setup
from Cython.Build import cythonize
setup(
name = 'Hello world app',
ext_modules = cythonize("test.py"),
)
然后我们就退回到目录下运行命令,就会在 windows 上生成 pyd 文件,或者 linux 上生成 so 文件:
python setup.py build_ext --inplace
最终生成如下文件:
其中,build 是生成过程使用到的临时文件。test.c 也是临时文件
注:可能会出现“Unable to find vcvarsall.bat”错误
参考这里
附:编译选定文件夹下所有 py 文件脚本
import os import re import shutil from distutils.core import Extension, setup from Cython.Build import cythonize from Cython.Compiler import Options # __file__ 含有魔术变量的应当排除,Cython虽有个编译参数,但只能设置静态。 exclude_so = ['__init__.py'] sources = ['.'] # 选定文件夹 extensions = [] for source in sources: # 递归遍历文件夹(深度优先) for dirpath, foldernames, filenames in os.walk(source): # 删除pyc文件 if '__pycache__' in foldernames: foldernames.remove('__pycache__') shutil.rmtree(os.path.join(dirpath, '__pycache__')) for filename in filter(lambda x: re.match(r'.*[.]py$', x), filenames): file_path = os.path.join(dirpath, filename) print(file_path, end='\t') if filename not in exclude_so: temp = re.sub(r'[/\\]', '.', file_path[:-3]).strip('.') print(temp) extensions.append( Extension(temp, [file_path], extra_compile_args=["-Os", "-g0"], extra_link_args=["-Wl,--strip-all"])) Options.docstrings = False compiler_directives = {'optimize.unpack_method_calls': False} setup( # cythonize的exclude全路径匹配,不灵活,不如在上一步排除。 # language_level是python的主版本号 ext_modules=cythonize(extensions, exclude=None, nthreads=20, quiet=True, build_dir='./build', language_level=3, compiler_directives=compiler_directives))
这个最方便只要一行代码就可以搞定。
https://github.com/cjrh/easycython
pip install easycython
这个模块也会自动安装依赖的 cython
将 .py 文件重命名为.pyx
运行命令
easycython *.pyx
上面会将当前文件夹下所有的.pyx 文件生成为.pyd (Linux 下是.so)
html 文件可以查看 .py 文件与 .c 文件的转换对照关系。
Nuitka:用户手册
100%兼容标准 python2/python3,静态编译你的 python 程序
鄙人还没来得及仔细研究。。。
同样的,最好重命名 pyd 文件名,将中间的部分删除,前后部分不要动
可以通过import test
使用该文件
个人建议:将核心的代码编译为 pyd 或者 so 文件,然后再写一个简单的 main.py 去 import 调用这些链接库文件,达到隐藏核心代码的作用。
另外,cython 在 jupyter notebook 里使用真的很刺激
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。