写在前面:文章内容为本人的学习过程,路过大神不喜勿喷!
一、编译成pyd的好处
- 性能提升:Python扩展模块通常由C语言或C++等语言编写,这使得它们在执行时可以比纯Python代码更高效。通过将Python代码编译成扩展模块,可以提高程序的执行速度,特别是对于一些计算密集型的任务。
- 保护源代码:将Python代码编译成扩展模块可以防止直接查看源代码,因为扩展模块是已编译的二进制文件。这可以增加代码的安全性,尽管并不是绝对的安全措施。
- 隐藏实现细节:通过将部分代码编写成C扩展,可以隐藏实现的细节并提供更高级别的Python接口。这使得项目可以更容易地维护和扩展,同时在性能上仍然能够受益。
- 与其他语言集成: 编译为扩展模块的代码可以更容易地与其他语言进行集成,尤其是C和C++。这对于与现有C/C++代码库的互操作性非常有用。
- 减少运行时解释开销: 由于扩展模块是已编译的二进制文件,它们的加载和执行通常会比解释Python代码更快,从而减少了运行时的解释开销。
二、基础步骤
1. 安装Visual Studio 2019
因为我这里只使用vs去编译py文件,并不用做开发,就只安装了这一项,如果有其他需要,请酌情安装。
安装好Visual Studio 2019后,需在环境变量【PATH】中配置一个路径。(根据你安装的位置和版本路径自行调整)
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build
这个目录下是VS的启动项脚本,大致可以理解成需要启动哪个编译环境。
经过多次编译试验,可以得出以下的使用方法:
如果本机为x64位机器,要将文件编译成64位pyd,则使用环境:vcvars64.bat
如果本机为x64位机器,要将文件编译成32位pyd,则使用环境:vcvarsamd64_x86.bat
如果本机为x86位机器,要将文件编译成32位pyd,则使用环境:vcvars32.ba
如果本机为x86位机器,要将文件编译成64位pyd,则使用环境:vcvarsx86_amd64.bat
2.安装必要的类库
pip install Cython == 0.29.24
pip install setuptools == 56.0.0
3.在要编译的py文件同目录下编写一个setup.py
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize(["test.py"], language_level='3'),
script_args=['build_ext', '--inplace'],
name='test.py'
)
4.执行命令
python setup.py
这里的python是64位的,生成的pyd就是64位的。
如果需要打包32位pyd,我使用conda构建了一个python的x86环境,切换至32位环境,生成即为32位的pyd。
等待命令执行结束,就可以在目录下看到一个类似 ***.cp37-win_amd64.pyd 文件。
此时,转化编译的过程就结束了。
但是,身为一个程序员,每次都要写一个setup.py文件,我表示太麻烦了。
三、自动化升级
1.编写一个自动化执行的的脚本py2pyd.py
import os, shutil, time, sys, glob
try:
from setuptools import setup
except:
print("Please install the Python module first:pip install cython")
sys.exit()
def py2pyd(path):
folder_path = os.path.dirname(path)
file_path = os.path.split(path)[1]
os.chdir(folder_path)
with open("setup.py", 'w') as f:
# 自动生成单独的setup.py文件
f.write(f"from setuptools import setup\n")
f.write(f"from Cython.Build import cythonize\n\n")
f.write(f"setup(\n")
f.write(f" ext_modules=cythonize(['{file_path}'], language_level='3'),\n")
f.write(f" script_args=['build_ext', '--inplace'],\n")
f.write(f" script_name='{file_path}',\n")
f.write(f" name='{file_path.split('.')[0]}',\n")
f.write(f")\n")
if machine == 'x64':
# 指定使用vs的x64位编译器来运行编译成64位的pyd
# 切换python环境到x64的环境
os.system('vcvars64.bat & set DISTUTILS_USE_SDK=1 & set MSSdk=1 & set "CFLAGS=/arch:AMD64" & set "LDFLAGS=/machine:X64" & activate py37 & python setup.py build_ext --inplace')
elif machine == 'x86':
# 指定使用vs的x64位的x86编译器来运行编译32位的pyd
# 切换python环境到x86的环境
os.system('vcvarsamd64_x86.bat & set DISTUTILS_USE_SDK=1 & set MSSdk=1 & set "CFLAGS=/arch:IA32" & set "LDFLAGS=/machine:X86" & activate py37_x86 & python setup.py build_ext --inplace')
filename = file_path.split('.py')[0]
time.sleep(2)
pyd_name = '%s\\%s.pyd' % (folder_path, filename)
if os.path.exists(pyd_name):
os.remove(pyd_name)
file_pyd = glob.glob(filename + "*.pyd")
print("Generated PYD: " + file_pyd[0])
os.rename(file_pyd[0], pyd_name)
os.remove('%s.c' % filename)
build_folder_path = os.path.join(folder_path, 'build')
shutil.rmtree(build_folder_path)
os.remove('setup.py')
if del_py == 'del':
os.remove(file_path)
# 遍历此目录下的所有py文件,包含子目录里的py
def get_all_file(path):
for root, dirs, files in os.walk(path):
for name in files:
if name.endswith(".py"):
file_path = os.path.join(root, name)
py2pyd(file_path)
if len(sys.argv) <= 4:
print("Command line parameter error...")
sys.exit()
# 获取命令行参数
# single_all: single单文件编译,all文件夹下所有文件编译
# del_py: 是否删除源py文件。del 删除, nodel 不删除
# machine: 打包成64位的还是32位的pyd
# paths: 文件或文件夹路径
single_all, del_py, machine, paths = sys.argv[1:5]
print(single_all, del_py, paths, machine)
if one_all == 'single':
py2pyd(paths)
else:
get_all_file(paths)
四、创建快捷使用工具
1. 打开PyCharm的Settings->Tools->External Tools,添加4个拓展工具。
(4个拓展工具的具体配置,请根据实际情况具体修改)
五、快速使用
在要编译的文件或文件夹上右键,选择External Tools中写好的工具,就可实现一键编译。
等待程序运行结束,在目录下就会得到同名的pyd。