为何要编写C扩展
- 保护核心代码
- 解决性能瓶颈
- 创建C一些特有的东西
环境:
- Linux .13.9-300.fc27.x86_64
- Python 3.6.3
开始
目标
这次我么来实习制作Python3的扩展,实现两个功能:
- 求一个整数的绝对值
- 求一个字符串的逆序
用c实现这个功能:
int my_abs(int n){
if(n<0)
n = n * -1;
return n;
}
void my_reverse(char *s){
if(s){
int len = strlen(s);
int i;
char t;
for(int i= 0;i<(len-1)/2;i++){
t = s[i];
s[i] = s[len-1-i];
s[len-1-i] = t;
}
}
}
利用样板来包装函数
//作用,接受python传的值,将结果计算后转为Python对象返回给python
//返回类型PyObject*,函数名:模块名_函数名
static PyObject *Extest_abs(PyObject *self,PyObject *args){
int num;
if(!(PyArg_ParseTuple(args,"i",&num))){ //获取Python传递的参数
return NULL;
}
return (PyObject*)Py_BuildValue("i",my_abs(num));//将结果转换为Python类型并返回
}
static PyObject *Extest_reverse(PyObject *self,PyObject *args){
char *s;
if(!(PyArg_ParseTuple(args,"z",&s))){
return NULL;
}
my_reverse(s);
return (PyObject*)Py_BuildValue("s",s);
}
为每个模块增加PyMethodDef ModuleMethods[]数组
static PyMethodDef ExtestMethods[] = {
{"abs",Extest_abs,METH_VARARGS},//函数名,包装函数名,解析
{"reverse",Extest_reverse,METH_VARARGS},
{NULL,NULL},//作为结束
};
编写初始化函数
static struct PyModuleDef ExtestModule = {
PyModuleDef_HEAD_INIT,//默认
"Extest",//模块名
NULL,
-1,
ExtestMethods //上面的数组
};
void PyInit_Extest(){
PyModule_Create(&ExtestModule);
}
总览
my_extend.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<Python.h>
int my_abs(int n){
if(n<0)
n = n * -1;
return n;
}
void my_reverse(char *s){
if(s){
int len = strlen(s);
int i;
char t;
for(i= 0;i<(len-1)/2;i++){
t = s[i];
s[i] = s[len-1-i];
s[len-1-i] = t;
}
}
}
void test(void){
printf("test my_abs:\n");
printf("|-8590|=%d\n",my_abs(-8590));
printf("|-0|=%d\n",my_abs(-0));
printf("|5690|=%d\n",my_abs(-5690));
printf("test my_reverse:\n");
char s0[10] = "apple";
char s1[20] = "I love you!";
char *s2 = NULL;
my_reverse(s0);
my_reverse(s1);
my_reverse(s2);
printf("'apple' reverse is '%s'\n",s0);
printf("'I love you!' reverse is '%s'\n",s1);
printf("null reverse is %s\n",s2);
}
//作用,接受python传的值,将结果计算后转为Python对象返回给python
//返回类型PyObject*,函数名:模块名_函数名
static PyObject *Extest_abs(PyObject *self,PyObject *args){
int num;
if(!(PyArg_ParseTuple(args,"i",&num))){
return NULL;
}
return (PyObject*)Py_BuildValue("i",my_abs(num));
}
static PyObject *Extest_reverse(PyObject *self,PyObject *args){
char *s;
if(!(PyArg_ParseTuple(args,"z",&s))){
return NULL;
}
my_reverse(s);
return (PyObject*)Py_BuildValue("s",s);
}
static PyObject *Extest_test(PyObject *self,PyObject *args){
test();
return (PyObject*)Py_BuildValue("");
}
//为每个模块增加PyMethodDef ModuleMethods[]数组
static PyMethodDef ExtestMethods[] = {
{"abs",Extest_abs,METH_VARARGS},
{"reverse",Extest_reverse,METH_VARARGS},
{"test",Extest_test,METH_VARARGS},
{NULL,NULL},
};
static struct PyModuleDef ExtestModule = {
PyModuleDef_HEAD_INIT,
"Extest",
NULL,
-1,
ExtestMethods
};
void PyInit_Extest(){
PyModule_Create(&ExtestModule);
}
编写用于编译安装的脚本
setup.py
from distutils.core import setup,Extension
MOD = 'Extest' #模块名
setup(name=MOD,ext_modules=[Extension(MOD,sources=['my_extend.c'])]) #源文件名
编译安装:
python3 setup.py build #提示没有Python.h百度
python3 setup.py install
使用:
>>> import Extest
>>> Extest.test()
test my_abs:
|-8590|=8590
|-0|=0
|5690|=5690
test my_reverse:
'apple' reverse is 'elppa'
'I love you!' reverse is '!uoy evol I'
null reverse is (null)
>>> Extest.abs(-0)
0
>>> Extest.reverse("abc")
'cba'