分类: IT业界
2022-01-09 17:41:37
真的好久没有分享技术文章了,主要是因为自己写的文章太过于小众,没想到自己竟然会分享这样一篇文章。这么一篇浓缩了自己多年来项目开发的实战经验。
不得不说,Python是一门很不错的编程语言。有时候,为了项目代码安全考虑,会尝试将一些源代码使用Python提供的C API的方式进行编写,再将其编译为Python模块的方式,从而在Python中直接调用。
对于Python的C API编写的代码,如果快速将其生成为Python对应的扩展库,如Linux下的.so或Windows下的.pyd。
主要有如下一些方法:
实际上这些方法经常是在一起使用的。
对于手动编写setup.py文件,指定对应的要引入的头文件及链接库,对于中大型项目是不推荐的。
在此之前,先使用如下的方式先生成1个C源文件:
pip install pyd-tpl pyd-tpl hello
该模块是自己编写用于快速生成Python的C扩展文件的一个库。安装完成后,我们调用pyd-tpl命令会在当前目录下生成1个hello.c源文件及setup_hello.py文件。
我们可以直接执行如下的方式进行编译并生成1个hello模块:
python setup_hello.py
这是第1种使用setuptools生成模块的方式。下面主要介绍后面两种方式。
首先我们运行下面的命令安装对应的库:
pip install scikit-build cmake
由于scikit-build依赖于cmake,我们可以直接使用pip的方式安装该工具。
安装完成后,接着我们在setup.py中编写如下的代码:
from skbuild import setup
之后在项目目录下新建1个CMakeLists.txt,其内容如下:
cmake_minimum_required(VERSION 3.11.0) project(hello VERSION 0.1.0) find_package(PythonExtensions REQUIRED) add_library("${PROJECT_NAME}" MODULE hello.c) python_extension_module("${PROJECT_NAME}") install(TARGETS hello LIBRARY DESTINATION hello)
这里我们设置我们项目的名称为hello,通过使用cmake提供的find_package指令寻找Python的扩展。之后通过add_library对hello.c文件编译,从而生成以项目名称命名的模块。
接着还需要在项目目录下创见1个pyproject.toml的文件,其内容如下:
[build-system] requires = ["setuptools", "wheel", "scikit-build", "cmake", "ninja"]
之后我们运行如下的命令即可实现整个过程的自动编译:
python setup.py build
不过这种方式有个不好的地方在于,如果系统无法正常识别你的编译器,比如在Windows下我使用的是Visual Studio 2015,它并没有很好的识别出来。只能在Visual Studio 2015工具提供的菜单窗口下运行才行。
其过程如下图所示:
在cmake版本3.12开始,提供了1个非常好用的Python_add_library命令,详情可以。借助该命令,我们可以快速生成Python的C扩展。
我们只需要在上述CMakeLists.txt中的内容进行如下的修改:
cmake_minimum_required(VERSION 3.12.0) project(hello VERSION 0.1.0) find_package (Python3 COMPONENTS Development) include_directories(${Python3_INCLUDE_DIRS}) Python3_add_library(hello MODULE hello.c)
该命令语法与add_library命令一样。在这里,我们使用的是Python3,因此在find_package命令中指定的使用Python3。
之后在Windows下创建1个build目录,并进入该目录中再执行如下命令:
mkdir build cd build
cmake -G "Visual Studio 14 2015" -A x64 .. cmake --build . --config Release
而对于Linux将相应的命令修改为:
cmake ..
make
通过cmake工具,这样我们就可以轻松地生成对应Python的hello.pyd及hello.so的C扩展。
后语
实际上,对于生成Python的C扩展还有1种方式就是将cmake与setup.py文件结合,通过setup.py文件中setup函数的cmdclass参数指定自定义1个cmake编译类。
实际上,在实际项目开发中,真的没必要搞得这么复杂,将1个非常简单的问题复杂化是得不偿失的。
这里就不展开介绍了,使用上述3种方法即可很轻松解决Python的C扩展编译的问题。其中最推荐使用第3种方式。