您现在的位置是:首页 > 技术教程 正文

【Python小技巧】加密又提速,把.py文件编译为.pyd文件(类似dll函数库),你值得拥有!

admin 阅读: 2024-03-23
后台-插件-广告管理-内容页头部广告(手机)

文章目录

  • 前言
  • 一、常见的Python文件格式有哪些?
  • 二、准备编译环境
    • 1. 安装cython
    • 2. 安装Microsoft C++ 生成工具
  • 三、编译.py文件为.pyd文件
    • 1. 编辑原始.py文件
    • 2. 准备setup.py文件
    • 3. 进行编译
  • 四、测试
  • 总结


前言


Python的脚本文件是开源的,若直接发布,就等于开源。对于个人使用或则公开源码的,没有问题。但对于分发部署,就有些不妥了。一则开源任何人都可以修改,可能不安全;二则效率没有编译后的高。所以,需要保护源码,特别是公司的产品,就需要对Python代码进行混淆加密保护。

那么,如何编译和加密呢?下面,我们就来说一说。

一、常见的Python文件格式有哪些?

Python常见的文件类型介绍:
.py python的源代码文件,可以直接编辑,直接运行
.pyc Python源代码import后,编译生成的字节码
.pyd Python的动态链接库(Windows平台dll),需要通过其它程序调用运行。

这里,我们重点说第三种文件形式。

二、准备编译环境

要将.py文件转为.pyd文件,我们需要两个工具,一个是cython,一个是微软的C++ 生成工具。下面我们一步一步来安装。

1. 安装cython

通过pip install cython -i https://pypi.tuna.tsinghua.edu.cn/simple 安装cython

(base) C:\Users\Administrator>pip install cython -i https://pypi.tuna.tsinghua.edu.cn/simple Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple Collecting cython Downloading https://pypi.tuna.tsinghua.edu.cn/packages/d2/49/9845f14b6716614c832535b67e3b491434d7fdecf510fcb6fe254f60a974/Cython-0.29.34-py2.py3-none-any.whl (988 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 988.1/988.1 kB 10.5 MB/s eta 0:00:00 Installing collected packages: cython Successfully installed cython-0.29.34 (base) C:\Users\Administrator>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2. 安装Microsoft C++ 生成工具

进入https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools/
下载生成工具,并在线安装。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
安装完成推出即可。

三、编译.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))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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"]))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3. 进行编译

通过cmd命令切换到文件所在目录,执行以下命令

python setup.py build_ext --inplace
  • 1

步骤及结果如下:

(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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

编译完成后生成my_fucn.cp310-win_amd64.pyd文件。正式发布使用时我们需要改回my_fucn.pyd才可以引用,将文件名中间“cp310-win_amd64”删除即可。且文件名称不可以修改成其它名字哦,否则会提示引用失败。

以上即整个pyd文件生成的方法。在python文件运行时将优先寻找调用.pyd文件。找不到会再寻找相对应的.py文件。

在这里插入图片描述

注意:如果出现如下提示,则表示您的编译Visual Studio编译环境未准备好。如果按步骤操作,应该不会出现。

(base) C:\Users\Administrator\pydtest>python setup.py build_ext --inplace
Compiling test.py because it changed.
[1/1] Cythonizing test.py
d:\ProgramData\anaconda3\lib\site-packages\Cython\Compiler\Main.py:369: FutureWarning: Cython directive ‘language_level’ not set, using 2 for now (Py2). This will change in a later release! File: C:\Users\Administrator\pydtest\test.py
tree = Parsing.p_module(s, pxd, full_module_name)
error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build Tools”: https://visualstudio.microsoft.com/visual-cpp-build-tools/

四、测试

为了比较运行速度,我们分别引入对应函数,并计算他们的用时。这里,我们把原始的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))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

同样的函数内容,同样的计算结果,结果如下:

cal_square1 耗时:5.30519700050354 使用py文件计算:平方累计= 99999980000001 cal_square2 耗时:4.141402721405029 使用pyd文件计算,平方累计= 99999980000001 请按任意键继续. . .
  • 1
  • 2
  • 3
  • 4
  • 5

经测试,可以发现,经过编译的函数,耗时明显减少了。

注意:如果运行出现如下提示,则检查以下你修改后的文件名。比如我编译为my_func.pyd,但改问你my_func_new.pyd,虽然引用路径没问题,但依然会报错:

Traceback (most recent call last): File "C:\Users\Administrator\pydtest\main.py", line 2, in <module> from my_func_new import square as square2 ImportError: dynamic module does not define module export function (PyInit_my_func_new) 请按任意键继续. . .
  • 1
  • 2
  • 3
  • 4
  • 5

总结

注意:这里还有个版本问题,刚才编译生成的my_fucn.cp310-win_amd64.pyd文件,即表示python3.10 win 64位操作系统环境。

在调用时也需要注意Python版本问题,调用环境要和编译的Python环境版本一致(如Python3.10编译,在Python3.10环境调用就行)。如果需要编译多个python版本的,可安装虚拟环境重复以上编译过程即可生成多个版本,方便不同环境下调用。

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索