Python是解释型语言,没有办法直接编译。个人认为python run time性能较弱在绝大多数场景下是个伪命题,性能弱鸡基本都是代码写的问题,跟人家python没啥关系。之所以会想着编译下,是为了保护源码。毕竟pyc这种东西一键反编译,自欺欺人不太好。OK, 下面直接贴代码~
from distutils.core import setup
from Cython.Build import cythonize
from shutil import copyfile, rmtree
import os
store_path = '你想要把项目编译后放的路径'
store_dir = '可以给编译后的项目一个新名字~'
for root, dirs, files in os.walk(".", topdown=True):
base_dir = os.path.join(store_dir, root.replace('.', store_dir))
if not os.path.exists(base_dir):
os.mkdir(base_dir)
for dir in dirs:
if not os.path.exists(os.path.join(base_dir, dir)):
os.mkdir(os.path.join(base_dir, dir))
# 上面的代码是按照项目的目录再创建一个空的目录
for file in files:
# 只编译 .py 文件
if file[-3:] == '.py':
setup(ext_modules=cythonize([os.path.join(root, file)], compiler_directives={'language_level': 3}),
script_args=["build_ext"])
# 轮询编译后的临时文件目录,抓取.so文件
for ro, d, fi in os.walk('./build'):
for f_name in fi:
if f_name[-3:] == '.so':
# 把.so文件复制到它应该在的位置
copyfile(os.path.join(ro, f_name), os.path.join(base_dir, f_name))
# 删除临时文件
os.remove(os.path.join(ro, f_name))
else:
# 把非py文件直接放到对应的位置
copyfile(os.path.join(root, file), os.path.join(base_dir, file))
# 轮询删掉所有临时生成的.c文件
for root, dirs, files in os.walk(".", topdown=True):
for file in files:
if file[-2:] == '.c':
os.remove(os.path.join(root, file))
# 删除build临时目录
rmtree('./build')
用法: 把这个文件丢进项目的根目录下,直接执行.
接下来说坑:
1. 文件名格式一定要标准, 什么中划线啊,特殊符号什么的,统统会报错。
2. 代码里的错误,就是红线一类,清理干净,不然也编译不成功。
3. 代码里的传参方式,入口文件直接引用的文件内,传参不允许出现(k=v)形式的传递,多个参数需要按位置传递,尽量向C靠近。
PS: 当确定要编译的化不妨提前注意代码的传参方式,避免kv传参。
4. 假如使用了Python注解,编译后的代码运行将变成强类型...举个栗子:
def func(name: str, op: str, val: str, belongs: dict, default: str) -> str:
...
就是这种写法,在纯python环境下,这个注解就是个注释...我们注解为str,想传啥就传啥...python不管的!但是,一旦我们用Cython编译后,它就不是python了,而是强类型的C。所以,严格意义上讲,这并不算是坑,因为python之所以慢,主要原因是它是弱类型,写的时候轻松,运行的时候解释器需要消耗资源和时间去解决类型的问题。
综上,Cython编译除了可以解决源码安全问题,不失为一种提升python性能的手段,前提是需要注意代码的一些写法。但还是不推荐使用。毕竟和pypi一样,也许性能确实可以提升,但是可靠性至今也没有得到验证。