场景
python的解释特性是将py编译为独有的二进制编码pyc 文件,然后对pyc中的指令进行解释执行,但是
pyc的反编译却非常简单,可直接反编译为源码,当需要将产品发布到外部环境的时候,源码的保护尤为重要。
一、Cpython介绍
Cython是一个编程语言,它通过类似Python的语法来编写C扩展并可以被Python调用,既具备了Python快速开发的特点,又可以让代码运行起来像C一样快,同时还可以方便地调用C library。
Cython是属于python的超集,用于编写python的c扩展语言。
pyx文件由 Cython 编译为.c文件,包含 python 扩展模块的代码。.c文件由 C 编译器编译为.so文件(或 Windows 上的.pyd)。
生成的.so文件或pyd文件是D语言(C/C++综合进化版本)生成的二进制文件,理论上很难反编译。
Note: 纯python源码被cython编译后,因为没有使用类型标注等cython语法,故编译后的动态链接库和可执行文件,主要依赖python的运行时,并不依赖C/C++运行时,主要由python解释器执行,多线程GIL的问题同样存在,性能提升有限,但对for循环、大的列表对象遍历性能有明显优化效果。
优势:资源丰富,适合快速开发。翻译成C后速度比较快
缺陷:无法支持JIT技术(导致纯python的执行速度比JAVA、JAVASCRIPT等要慢,于是有了PyPy)
二、安装
# 安装cython
pip3 install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple cython
# 安装c编译器(linux需安装python-devel, gcc)
centos: yum install python-devel gcc
ubuntu: apt-get install build-essential
三、使用方法
先用cython将python语言代码转换为c语言代码,然后用c编译器(gcc)生成可执行文件或动态链接库
。
通过
shell 或
python脚本的方式
,将项目启动的入口py编译成可执行文件,将项目的其他.py文件编译成.so(__init__.py除外)
Note:
__init__.py文件定义了python的包结构,
为了使cython编译后的.so
能按照正常路径import,__init__.py不能被编译
,故为了保护代码,整个项目的所有__init__.py文件不建议放业务相关代码。
3.1、单个文件的编译示例-linux
目录结构如下:
test/├── test.py├── main.py
test.py:
def hello():
print('hello!')
main.py:
#!/usr/bin/python3.8
from test import hello
if __name__ == "__main__":
hello()
3.1.1、将启动main.py编译成二进制可执行文件main
# 可通过文件头的 #!/usr/bin/env python 标记是否程序启动文件
>>>python3版本下:#!/usr/bin/python3.8
main.py ---> main.c ---> main.o ---> main
# step1: 将python代码翻译成c代码(main.py -> main.c)cython -D -3 --directive always_allow_keywords=true --embed main.py# step2: 将c代码编译为目标文件(main.c -> main.o)gcc -c main.c -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I /usr/include/python3.8 -L /usr/bin -lpython3.8 -o main.o# step3: 将目标文件编译为二进制可执行文件(main.o -> main)gcc main.o -I /usr/include/python3.8 -L /usr/bin -lpython3.8 -o main
main.py ---> main.c ---> main
# step1: 将python代码翻译成c代码(main.py -> main.c)cython -D -3 --directive always_allow_keywords=true --embed main.py# step2: 将c代码编译为二进制可执行文件(main.c -> main)gcc main.c -I /usr/include/python3.8 -L /usr/bin -lpython3.8 -o main
3.1.2、将test.py编译为动态链接库test.so
>>>python3版本下:test.py ---> test.c ---> test.so
# step1: 将python代码翻译成c代码(test.py -> test.c)cython -D -3 --directive always_allow_keywords=true test.py# step2: 将c代码编译为linux动态链接库文件(test.c -> test.so)gcc test.c -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I /usr/include/python3.8 -L /usr/bin -lpython3.8 -o test.so
cython参数说明:
-D, --no-docstrings, Strip docstrings from the compiled module.
-o, --output-file <filename> Specify name of generated C file
-2 Compile based on Python-2 syntax and code semantics.
-3 Compile based on Python-3 syntax and code semantics.
gcc参数说明:
-shared:
编译动态库时要用到
-pthread:
在Linux中要用到多线程时,需要链接pthread库
-fPIC:
作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),
则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意
位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
-fwrapv:
它定义了溢出时候编译器的行为——采用二补码的方式进行操作
-O参数
这是一个程序优化参数,一般用-O2就是,用来优化程序用的
-O2:
会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。
-O3: 在O2的基础上进行更多的优化
-Wall:
编译时 显示Warning警告,但只会显示编译器认为会出现错误的警告
-fno-strict-aliasing:
“-fstrict-aliasing”表示启用严格别名规则,“-fno-strict-aliasing”表示禁用严格别名规则,当gcc的编译优化参数为“-O2”、“-O3”和“-Os”时,默认会打开“-fstrict-aliasing”。
-I (大写的i):
是用来指定头文件目录
-I /home/hello/include表示将/home/hello/include目录作为第一个寻找头文件的目录,寻找的顺序是:/home/hello/include-->/usr/include-->/usr/local/include
-l:
-l(小写的 L)参数就是用来指定程序要链接的库,-l参数紧接着就是库名,把库文件名的头lib和尾.so去掉就是库名了,例如我们要用libtest.so库库,编译时加上-ltest参数就能用上了
3.2、单个文件的编译示例-windows
windows系统使用cython需要确保
已安装C/C++编译器且环境变量正确配置,cython能找到编译器。windows系统可使用MSVC(Microsoft Visual C/C++)或者clang编译器。
将test.py编译为动态链接库test.pyd:
>>>python3版本下:test.py ---> test.c ---> test.pyd
# step1: 将python代码翻译成c代码(test.py -> test.c)cython -D -3 --directive always_allow_keywords=true test.py# step2: 将c代码编译为windows动态链接库文件(test.c -> test.pyd)cythonize -i test.c最后得到windows下的动态链接库文件test.cp39-win_amd64.pyd,还需要将文件名重命名为test.pyd。
cythonize参数说明:
-b, --build build extension modules using distutils
-i, --inplace build extension modules in place using distutils(implies -b),即将编译后的扩展模块直接放在与test.py同级的目录中。
四、python编译可执行文件与动态链接库
五、参考
(8条消息) python性能优化和源码保护-编译整个项目_python编译当前项目_pushiqiang的博客-CSDN博客
(2条消息) gcc编译_青霄的博客-CSDN博客
Cython: C-Extensions for Python
Cython的简单使用 - 小得盈满 - 博客园 (cnblogs.com)
cython编译的so和c语言编译的so本质上有什么区别? - 知乎 (zhihu.com)
github: 使用Cython编译整个python项目