一般来说,所有能被整合或导入到其他Python脚本中的代码,都可以称为扩展。你可以用纯Python来写扩展,也可以用C/C++之类的编译型语言来写扩展,甚至可以用java,C都可以来写 python扩展。Python的一大特点是,扩展和解释器之间的交互方式域普通的Python模块完全一样,Python的模块导入机制非常抽象,抽象到让使用模块的代码无法了解到模块的具体实现细节。
Python进行扩展的主要原因有三点:(1)添加额外的Python语言的核心部分没有提供的功能 (2)为了提升性能瓶颈的效率(3)保持专有源代码私密,把一部分代码从Python转到编译语言可以保持专有源代码私密
方法一:利用C API进行C扩展
这种方法是最基本的,包括三个步骤:1.创建应用程序代码 2.利用样板来包装代码 3.编译
1.创建一个Extest.c文件 包含两个C函数
Extest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 10
int fac(int n) {
if (n < 2)
return 1;
return n * fac(n - 1);
}
//字符串反转
char *reverse(char *s) {
register char t;
char *p = s;
char *q = (s + (strlen(s) - 1));
while (p < q) {
t = *p;
*p++ = *q;
*q-- = t;
}
return s;
}
<strong><span style="font-size:18px;">
</span></strong>
2.用样板来包装你的代码
使用样板分为4步:
1.添加Python的头文件
2.为每一个模块的每个函数增加一个形如PyObject* Module_func()的包装函数
3.为每一个模块增加一个形如PyMethodDef ModuleMethods[]的数组
4.增加模块初始化函数 void initModule()
第二步的处理需要一些技巧,你需要为所有想Python环境访问的函数增加一个静态函数。函数的返回值类型为 PyObject*,在Python的C语言扩展接口中,大部分函数都有一个或者多个参数为PyObject指针类型,并且返回值也大都为PyObject指针. 包装函数的用处是先把Python的值传递给C,然后调用C函数把函数的计算结果转换成Python 对象,然后返回给Python
第三步每一个数组都包含一个 函数信息,包括函数Python的名字,相应的包装函数的名字以及一个METH_VARARGS常量(表示参数以元组的形式传入)
Extest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Python.h" //包含python头文件
#define BUFSIZE 10
int fac(int n) {
if (n < 2)
return 1;
return n * fac(n - 1);
}
char *reverse(char *s) {
register char t;
char *p = s;
char *q = (s + (strlen(s) - 1));
while (p < q) {
t = *p;
*p++ = *q;
*q-- = t;
}
return s;
}
//fac函数的包装函数
static PyObject *
Extest_fac(PyObject *self, PyObject *args) {
int num;
if (!(PyArg_ParseTuple(args, "i", &num))) {
return NULL;
}
return (PyObject *)Py_BuildValue("i", fac(num));
}
//reverse函数的包装函数
static PyObject *
Extest_doppel(PyObject *self, PyObject *args) {
char *orignal;
char *reversed;
PyObject * retval;
if (!(PyArg_ParseTuple(args, "s", &orignal))) {
return NULL;
}
retval = (PyObject *)Py_BuildValue("ss", orignal, reversed=reverse(strdup(orignal)));
free(reversed);
return retval;
}
//为模块创建一个函数信息的数组
static PyMethodDef
ExtestMethods[] = {
{"fac", Extest_fac, METH_VARARGS},
{"doppel", Extest_doppel, METH_VARARGS},
};
//增加模块初始化函数
void initExtest() {
Py_InitModule("Extest", ExtestMethods);
}
在编译阶段需要创建一个setup.py,通过setup.py来编译和链接代码。这一步完成后就就可以直接导入这个扩展的模块了.在setup.py中需要导入distutils包。首先你要为每一个扩展创建一个Extension实例,然后编译的操作主要由setup()函数来完成,它需要两个参数:一个名字参数表示要编译哪个东西,一个列表表示要编译的对象
setup.py
<pre name="code" class="python">from distutils.core import setup, Extension
MOD = 'Extest'
setup(name=MOD, ext_modules=[
Extension(MOD, sources=['Extest.c'])])
运行setup.py :执行命令 python setup.py build
执行的结果 是在当前目录中生成一个build 目录,在此目录下有一个Extest.so文件,然后就可以在脚本中import这个模块了