python扩展
2015-06-11 10:51
746 查看
当python的基本功能无法满足要求,或者是为了保密源代码(.py)、遇到性能瓶颈时,我们常常要扩展python,扩展语言可以是C/C++、Java、C#等。
为python创建扩展需要三个主要的步骤:创建应用程序代码;利用样板来包装代码;编译与测试。
1、 创建应用程序代码
我们创建一个C代码PythonEx.c,实现两个函数fac()和reverse(),分别用来求阶乘和逆转字符串,test()函数是用来测试fac()和reverse()基本功能的,以防问题带入python。
2、 用样板包装代码
样板是扩展代码与python解释器之间进行交互的桥梁,主要分为以下4步。
a.包含python的头文件。
找到python头文件位置并确保编译器的访问权限,然后在代码中inlcude这个头文件,如下:
b.为每个模块的每一个函数增加一个型如PyObject* Module_func()的包装函数。
这部分需要为所有想被python环境访问的函数都增加一个静态的函数,函数的返回值类型为PyObject*,函数名前面要加上模块名和一个下划线。包装函数的用处就是先把python的值传递给C,然后调用我们想要调用的相关函数,当这个函数完成要返回python的时候,把函数的计算结果转换成python的对象,然后返回给python。那么,在从python到C的转换就用PyArg_Parse*系列函数,在从C转到python的时候,就用Py_BuildValue()函数。
下面我们包装fac()和reverse()函数,假设其模块名为CustomPy,如下:
c.为每个模块增加一个型如PyMethodDef ModuleMethods[]的数组。
完成包装函数后,把他们加入到一个数组中,以便于python解释器能够导入并调用它们。每一个数组都包含了函数在python中的名字、相应的包装函数的名字以及一个METH_VARARGS常量,这个常量表示参数以tuple形式传入。最后是一个NULL数组表示列表结束。
d.增加模块初始化函数void initModule()
这部分代码在模块被导入的时候被解释器调用。
另外,创建扩展还可以先写包装代码,使用桩函数、测试函数或哑函数,在开发过程中慢慢地把这些函数用有实际功能的函数替换。
3、编译与测试
为了让新python扩展能被创建,需要把它们与python库放在一起编译,以前可能要用到Makefile,现在使用distutils模块就可以了,能够方便地编译、安装和分发这些模块、扩展和包,只需创建一个setup.py脚本就行。
下面直接运行setup.py脚本来创建自定义模块,或者是install到python环境里,最终会生成一个so文件,接着import这个模块就可以使用了。
还有两点需要注意的是,python的垃圾自动回收策略是引用计数,以及线程安全操作,这些都可以加入到自定义的模块中。
上面的例子中用C扩展了Python,我们还可以用Java扩展Jython,使用C#或者VB .NET扩展IronPython。如果在win32下,python还可以使用其COM(市场名字为ActiveX)操作Microsoft Office。
为python创建扩展需要三个主要的步骤:创建应用程序代码;利用样板来包装代码;编译与测试。
1、 创建应用程序代码
我们创建一个C代码PythonEx.c,实现两个函数fac()和reverse(),分别用来求阶乘和逆转字符串,test()函数是用来测试fac()和reverse()基本功能的,以防问题带入python。
// PythonEx.c #include <stdio.h> #include <stdlib.h> #include <string.h> int fac(int n) { if (n < 2) { return (1); } else { return (n) * fac(n - 1); } } char* reverse(char *s) { register char t; register char *p = s; register char *q = (s + strlen(s) -1); while (p < q) { t = *p; *p++ = *q; *q-- = t; } return s; } int test() { char s[BUFSIZ]; printf("3! = %d\n", fac(3)); printf("6! = %d\n", fac(6)); printf("9! = %d\n", fac(9)); strcpy(s, "abcdefg"); printf("'abcdefg' after reversing is '%s'\n", reverse(s)); strcpy(s, "python"); printf("'python' after reversing is '%s'\n", reverse(s)); return 0; }
2、 用样板包装代码
样板是扩展代码与python解释器之间进行交互的桥梁,主要分为以下4步。
a.包含python的头文件。
找到python头文件位置并确保编译器的访问权限,然后在代码中inlcude这个头文件,如下:
#include “Python.h”
b.为每个模块的每一个函数增加一个型如PyObject* Module_func()的包装函数。
这部分需要为所有想被python环境访问的函数都增加一个静态的函数,函数的返回值类型为PyObject*,函数名前面要加上模块名和一个下划线。包装函数的用处就是先把python的值传递给C,然后调用我们想要调用的相关函数,当这个函数完成要返回python的时候,把函数的计算结果转换成python的对象,然后返回给python。那么,在从python到C的转换就用PyArg_Parse*系列函数,在从C转到python的时候,就用Py_BuildValue()函数。
下面我们包装fac()和reverse()函数,假设其模块名为CustomPy,如下:
static PyObject* CustomPy_fac(PyObject *self, PyObject *args) { int num; if (!PyArg_ParseTuple(args, "i", &num)) { // i i.e. int->int return NULL; } return (PyObject*)Py_BuildValue("i", fac(num)); // i i.e. int->int } static PyObject* CustomPy_reverse(PyObject *self, PyObject *args) { char *orig_str; // original char *dup_str; // reversed PyObject *retval; if (!PyArg_ParseTuple(args, "s", &orig_str)) { // s i.e. str->char* return NULL; } retval = (PyObject*)Py_BuildValue( "ss", // s i.e. char*->str orig_str, dup_str = reverse(strdup(orig_str))); // strdup free(dup_str); // free after strdup return retval; // return tuple(orig_str, dup_str) } static PyObject* CustomPy_test(PyObject *self, PyObject *args) { test(); return (PyObject*)Py_BuildValue(""); }
c.为每个模块增加一个型如PyMethodDef ModuleMethods[]的数组。
static PyMethodDef CustomPyMethods[] = { {"fac", CustomPy_fac, METH_VARARGS}, {"reverse", CustomPy_reverse, METH_VARARGS}, {"test", CustomPy_test, METH_VARARGS}, {NULL, NULL}, };
完成包装函数后,把他们加入到一个数组中,以便于python解释器能够导入并调用它们。每一个数组都包含了函数在python中的名字、相应的包装函数的名字以及一个METH_VARARGS常量,这个常量表示参数以tuple形式传入。最后是一个NULL数组表示列表结束。
d.增加模块初始化函数void initModule()
void initCustomPy() { Py_InitModule("CustomPy", CustomPyMethods); }
这部分代码在模块被导入的时候被解释器调用。
另外,创建扩展还可以先写包装代码,使用桩函数、测试函数或哑函数,在开发过程中慢慢地把这些函数用有实际功能的函数替换。
3、编译与测试
为了让新python扩展能被创建,需要把它们与python库放在一起编译,以前可能要用到Makefile,现在使用distutils模块就可以了,能够方便地编译、安装和分发这些模块、扩展和包,只需创建一个setup.py脚本就行。
#!/usr/bin/env python from distutils.core import setup, Extension MOD = 'CustomPy' setup(name = MOD, ext_modules = [Extension(MOD, sources = [PythonEx.c])])
下面直接运行setup.py脚本来创建自定义模块,或者是install到python环境里,最终会生成一个so文件,接着import这个模块就可以使用了。
还有两点需要注意的是,python的垃圾自动回收策略是引用计数,以及线程安全操作,这些都可以加入到自定义的模块中。
上面的例子中用C扩展了Python,我们还可以用Java扩展Jython,使用C#或者VB .NET扩展IronPython。如果在win32下,python还可以使用其COM(市场名字为ActiveX)操作Microsoft Office。
相关文章推荐
- python脚本积累
- python日志模块
- windows下python画图matplotlib
- Python 时间和日期模块的常用例子
- Python正则表达式
- Python WxPython 的安装以及使用
- Python WxPython 的安装以及使用
- python3 tkinter教程
- Python基础教程笔记——抽象
- Python基础教程笔记——基础知识
- Python基础教程笔记——条件,循环和其他语句
- Python 学习笔记4
- python学习笔记
- python中正则表达式
- php调用Python接口的方法
- Python语言实现机器学习的K-近邻算法
- Python基础教程笔记——字典:当索引不好用时
- Python基础教程笔记——列表和元组
- Python基础教程笔记——使用字符串
- Python正则表达式