Python的扩展模块是一种Python模块,它允许开发人员使用C或C++编写代码来扩展Python解释器的功能。这些模块不仅可以定义新的函数,还可以定义新的对象类型及其方法。Python提供了一些工具和API,使得扩展模块的开发变得更加容易。例如,Python提供了一个名为ctypes的库,它允许开发人员在Python中调用C函数和使用C数据类型。此外,Python还提供了一个名为Cython的编译器,它允许开发人员将Python代码转换为C或C++代码,并将其编译为共享库或动态链接库。这些库可以像普通Python模块一样导入到Python解释器中,并使用其中定义的函数和类。
Python/C API可能是被最广泛使用的方法。它不仅简单,而且可以在C代码中操作你的Python对象。但要使用这种方法,需要用特定的方式来编写C代码,所以C代码不是原生的C(大伙要适应一下),这样才可以供python去调用。
生成扩展模块的流程可以概括为以下几个步骤:
1. 导入必要的头文件和库文件,例如Python.h。
2. 定义一个或多个C函数,这些函数将作为扩展模块中的方法。
3. 创建一个包含方法定义的结构体,其中每个方法都有一个名称、对应的C函数指针以及参数类型和返回值类型。
4. 定义一个模块属性结构体,指定模块的名称、父模块(如果有)以及其他属性。
5. 编写模块的初始化函数,该函数在模块被加载时调用。初始化函数通常会执行一些设置和初始化操作,然后返回一个指向模块对象的指针。
6. 使用`PyModule_Create`函数创建模块对象,该函数接受模块属性结构体作为参数。
7. 返回模块对象指针。
通过以上步骤,我们可以将C代码编写的扩展模块编译成Python可调用的共享库文件,从而可以在Python中使用该扩展模块提供的功能。
下面通过一个简单的例子,对上面的过程举例说明。
test.py
import Test
x = 1
print(Test.add_one(x))
而这里的Test模块,则是需要我们自己用C语言写,test.c文件代码如下:
#include <Python.h>
int add_one(int a){
return a + 1;
}//这是C的原生函数,实现+1功能
static PyObject * py_add_one(PyObject *self, PyObject *args){
int num;
if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
return PyLong_FromLong(add_one(num));
}//这一串代码要实现的功能如下,按照python规定的调用方式:
//1.定义一个新的静态函数,接收2个PyObject *参数,返回1个PyObject *值
//2.PyArg_ParseTuple方法将python输入的变量变成C的变量,即上述args→num
//3.紧接着调用C原生函数add_one,传入num
//4.最后将调用返回的C变量,转换为PyObject*或其子类,并通过PyLong_FromLong方法,返回python值
static PyMethodDef Methods[] = {
{"add_one", py_add_one, METH_VARARGS},
{NULL, NULL}
};//这串代码的目的是创建一个数组,来指明Python可以调用这个扩展函数:
//其中"add_one",代表编译后python调用时希望使用的函数名。而py_add_one,代表要调用当前C代码中的哪一个函数,即static PyObject * py_add_one。METH_VARARGS,代表函数的参数传递形式,主要包括位置参数和关键字参数两种
//最后如果希望添加新的函数,则在最后的{NULL, NULL}里按同样格式填写新的调用信息,比如加一些求和求乘积函数
static struct PyModuleDef cModule = {
PyModuleDef_HEAD_INIT,
"Test", /*模块名,即是生成模块后的名字,如numpy,opencv等等*/
"", /* 模块文档,可以为空NULL */
-1, /* 模块中每个解释器的状态大小,模块在全局变量中保持状态,则为-1 */
Methods//即上一步定义的Methods,说明了可调用的函数集
};//创建module的信息
PyMODINIT_FUNC PyInit_Test(void){ return PyModule_Create(&cModule);}
将C文件放置在python项目的同文件目录下,然后编写setup.py文件,setup.py是用来将C打包成模块的脚本文件,代码如下:
from distutils.core import setup, Extension #这里要用到distutils库
module1 = Extension('Test', sources = ['test.c']) #打包文件为test.c,这里没有设置路径,所有.c文件和setup.py放在同一目录下
setup (name = 'Test', #打包名
version = '1.0', #版本
description = 'This is a demo package', #说明文字
ext_modules = [module1])
python setup.py build --compiler msvc #编译代码
python setup.py build --compiler msvc install #编译代码并直接将包放入当前python环境的包的路径以供调用
执行python脚本结果、
大家可以看一下截图中的目录结构,简单起见,我把相关的python文件,c文件都放在同级目录下,免得在写代码的时候include,import麻烦,大家在实际项目中可以多级目录。