Python的脚本文件是开源的,若直接发布,就等于开源。对于个人使用或则公开源码的,没有问题。但对于分发部署,就有些不妥了。一则开源任何人都可以修改,可能不安全;二则效率没有编译后的高。所以,需要保护源码,特别是公司的产品,就需要对Python代码进行混淆加密保护。
那么,如何编译和加密呢?下面,我们就来说一说。
一、常见的Python文件格式有哪些?
Python常见的文件类型介绍:
.py python的源代码文件,可以直接编辑,直接运行
.pyc Python源代码import后,编译生成的字节码
.pyd Python的动态链接库(Windows平台dll),需要通过其它程序调用运行。
这里,我们重点说第三种文件形式。
二、准备编译环境
要将.py文件转为.pyd文件,我们需要两个工具,一个是cython,一个是微软的C++ 生成工具。下面我们一步一步来安装
1. 安装cython
通过pip install cython
2. 安装Microsoft C++ 生成工具
进入Microsoft C++ 生成工具 - Visual Studio
下载生成工具,并在线安装。
安装完成推出即可。
三、编译.py文件为.pyd文件
1. 编辑原始.py文件
这里我们写一个计算平方的函数.py文件,将以下文件保存为my_func.py。
注:这里第3行添加 # cython: language_level=3,以表示在Python3环境进行编译。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# cython: language_level=3
def square(x):
return int(x)**2
if __name__ == '__main__':
print(square(6))
2. 准备setup.py文件
在同目录下编写setup.py文件,将my_func.py写到最后一行。如果有多个.py文件,以逗号为间隔全部写上,可一次性编译。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# here put the import lib
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize(["my_func.py"]))
3. 进行编译
通过cmd命令切换到文件所在目录,执行以下命令
python setup.py build_ext --inplace
python setup.py build_ext --inplace
步骤及结果如下:
(base) C:\Users\Administrator>cd pydtest
(base) C:\Users\Administrator\pydtest>python setup.py build_ext --inplace
Compiling my_func.py because it changed.
[1/1] Cythonizing my_func.py
my_func.c
正在创建库 build\temp.win-amd64-cpython-310\Release\my_func.cp310-win_amd64.lib 和对象 build\temp.win-amd64-cpython-310\Release\my_func.cp310-win_amd64.exp
正在生成代码
已完成代码的生成
(base) C:\Users\Administrator\pydtest>
编译完成后生成my_fucn.cp310-win_amd64.pyd文件。正式发布使用时我们需要改回my_fucn.pyd才可以引用,将文件名中间“cp310-win_amd64”删除即可。且文件名称不可以修改成其它名字哦,否则会提示引用失败。
以上即整个pyd文件生成的方法。在python文件运行时将优先寻找调用.pyd文件。找不到会再寻找相对应的.py文件。
四、测试
为了比较运行速度,我们分别引入对应函数,并计算他们的用时。这里,我们把原始的my_func.py文件放到了backup目录,后期使用backup.my_func导入。
我们编辑一个main.py,内容如下:
这里引入使用装饰器,方便测算函数运行时间。只需要在函数前面加上@timer即可。
# 引用原始的.py文件
from backup.my_func import square as square1
# 引用编译后的.pyd文件
from my_func import square as square2
# 装饰器(计算执行耗时)
def timer(func):
import time
def deco(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(''.join([func.__name__,' 耗时:',str(stop-start)]))
return res
return deco
@timer
def cal_square1(x):
total = 0
for i in range(x):
y = square1(i)
total += y
return y
@timer
def cal_square2(x):
total = 0
for i in range(x):
y = square2(i)
total += y
return y
if __name__ == '__main__':
x = 10000000
print('使用py文件计算:平方累计=',cal_square1(x))
print('使用pyd文件计算,平方累计=',cal_square2(x))
同样的函数内容,同样的计算结果,结果如下:
cal_square1 耗时:5.30519700050354
使用py文件计算:平方累计= 99999980000001
cal_square2 耗时:4.141402721405029
使用pyd文件计算,平方累计= 99999980000001
请按任意键继续. . .
经测试,可以发现,经过编译的函数,耗时明显减少了。