c语言调用python模块,c++扩展python
最近看到一些关于pytorh的东西,不得不承认现在很火,有点好奇。下载完代码,发现计算部分基本都是用C写的,真的让我对这个所谓的Python拼音编写框架或者库有点失落。仔细一看,大意是逻辑控制部分是用Python写的。计算部分是用C语言编写的扩展模块,这个扩展模块的界面是纯C语言编写的。不得不说Python和C真的是从C开发加工出来的,不好用的时候就调用C,然后就搞定了。让我们言归正传。想法是把C和SSE,CUDA连接起来。说白了,C扩展模块有一部分是在CPU上运行的。一部分是C和SSE(向量计算)操作的,另一部分是CUDA在GPU上操作的,所以对这个C扩展很好奇,本文阐述一下。
本文基于Python Cookbook第三版15.2中编写的简单C语言扩展模块。
目录结构如下:
其中,示例文件夹如下:
Sample.c sample.h是标准的C语言程序和头文件,如下所示:
/* sample.h */
extern int gcd(int x,int y);
extern int in_mandel(double x0,double y0,int n);
extern int divide(int a,int b,int * remainder);
/* sample.c */
#包含数学. h
/*计算最大公约数*/
int gcd(int x,int y) {
int g=y;
while (x 0) {
g=x;
x=y % x
y=g;
}
返回g;
}
/*测试(x0,y0)是否在Mandelbrot集合中*/
int in_mandel(double x0,double y0,int n) {
double x=0,y=0,xtemp
while (n 0) {
xtemp=x * x-y * y x0;
y=2 * x * y y0
x=xtemp
n-=1;
if (x*x y*y 4)返回0;
}
返回1;
}
/*将两个数相除*/
int divide(int a,int b,int *remainder) {
int quot=a/b;
*余数=a % b;
返回quot
}
gcc共享fPIC sample.c -o libsample.so
您可以将这个标准的C文件编译成一个标准的动态库,即libsample.so
这个操作在本文中没有任何意义,只是为了证明C语言文件是正确的。
在前一个目录中执行以下命令:
python 3 setup . py build _ ext-in place
其中sample . cpython-35m-x86 _ 64-Linux-GNU . so是编译后的动态链接库,是我们的扩展模块。
上述编译操作中setup.py文件的内容如下:
# setup.py
从distutils.core导入设置,扩展
设置(name=sample ,
ext_modules=[
扩展(示例),
[sample/sample.c , pysample.c],
include_dirs=[sample],
)
]
其中name是指编译后Python包的名称,没有实际意义,因为我们只需要这个模块是。所以编译后归档。
[sample/sample.c , pysample.c]是我们写的C语言代码,其中sample.c是我们写的函数代码,pysample.c负责C语言和Python的语言交互。
扩展名( sample )是模块的名称,即。所以归档吧。名称不能随意更改,必须与pysample.c的定义相同,否则Python中无法识别模块的内容。
实际上,对于编写Python的C语言扩展来说,重要或困难的不一定是函数代码。这里是sample.c代码,但是负责两种环境下数值转换的代码,即pysample.c这个代码才是真正的扩展需要解决的。
在某种程度上,pysample.c更像是一个接口,就像头文件对于标准C文件一样。
测试编译后扩展是否可用:
#示例. py
导入样本
print(sample.gcd(35,42))
print(sample.in_mandel(0,0,500))
print(sample.in_mandel(2.0,1.0,500))
print(sample.divide(42,8))
=========================================================================
下面是核心代码,即接口代码或环境转换代码:
pysample.c
#包含“Python.h”
#include sample.h
/* int gcd(int,int) */
静态PyObject *py_gcd(PyObject *self,PyObject *args) {
int x,y,result
如果(!PyArg_ParseTuple(args, ii ,x,y)) {
返回NULL
}
结果=gcd(x,y);
返回Py_BuildValue(i ,result);
}
/* int in_mandel(double,double,int) */
静态py object * py _ in _ Mandel(py object * self,PyObject *args) {
double x0,y0;
int n;
(同Internationalorganizations)国际组织结果;
如果(!PyArg_ParseTuple(args, ddi ,x0,y0,n)) {
返回空
}
result=in_mandel(x0,y0,n);
返回Py_BuildValue(i ,result);
}
/* int divide(int,int,int *) */
静态py object * py _ divide(py object * self,PyObject *args) {
int a,b,商,余数;
如果(!PyArg_ParseTuple(args, ii ,a,b)) {
返回空
}
商=除(甲、乙、余数);
返回Py_BuildValue((ii),商,余数);
}
/*模块方法表*/
静态PyMethodDef SampleMethods[]={
{gcd ,py_gcd,METH_VARARGS,最大公约数 },
{in_mandel ,py_in_mandel,METH_VARARGS, Mandelbrot test},
{divide ,py_divide,METH_VARARGS,整数除法 },
{ NULL,NULL,0,NULL}
};
/*模块结构*/
静态结构PyModuleDef samplemodule={
PyModuleDef_HEAD_INIT,
样本,/*模块名称*/
样本模块,/* Doc字符串(可能为空)*/
-1,/*每个解释器状态的大小或-1 */
样品方法/*方法表*/
};
/*模块初始化功能*/
功能皮莫迪尼特
PyInit_sample(void) {
返回PyModule _ Create(示例模块);
}
接口文件中:
/*模块初始化功能*/
功能皮莫迪尼特
PyInit_sample(void) {
返回PyModule _ Create(示例模块);
}是接口的初始化代码,也是接口代码中唯一的一个非静电函数,该函数非静态允许在计算机编程语言类中调用,也就是导入样本时候的操作。
返回类型功能皮莫迪尼特说明返回的是模块的创建对象,也就是导入样本中的模块。
PyInit_sample该函数名的前部分是固定不变的,PyInit_是固定格式,后面跟着的样品则是模块名称PyModule_Create是具体的模块创建代码,其中的参数则是模块结构的说明变量的地址,也就是本文的样本模块变量。1/*模块结构*/
静态结构PyModuleDef samplemodule={
PyModuleDef_HEAD_INIT,
样本,/*模块名称*/
样本模块,/* Doc字符串(可能为空)*/
-1,/*每个解释器状态的大小或-1 */
样品方法/*方法表*/
};其中,"样本"是模块名称,"样本模块"是模块的文档, -1 表示该模块不能被多个计算机编程语言解释器同时公用(也就是不能保证并发访问的安全性)。
重点的是样本方法这个是模块中方法的描述变量,也就是方法表。
模块的描述变量中比较要人不理解的是这个变量PyModuleDef_HEAD_INIT,这个变量形式上来看应该是一个宏定义的变量,这个变量的存在好像并没有什么意义。
为了进一步了解宏定义变量PyModuleDef_HEAD_INIT查询了以下资料:
https://份文件。python。org/3/c-API/module。超文本标记语言
PyModuleDef
模块定义结构,包含创建模块对象所需的所有信息。对于每个模块,通常只有一个这种类型的静态初始化变量。
PyModuleDef_Base m_base始终将此成员初始化为PyModuleDef_HEAD_INIT .
由此可以看出这个变量在计算机编程语言的C扩展中是固定不变的,并没有必要继续深究,固定如此就好。
不过好奇心使然又接着继续研究了以下,发现下面的资料:
已经编译好的代码附上:
环境为Ubuntu 16.04 x86_64
gcc5.0
python3.5
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。