Python提供了接口API,通过使用API函数可以编写Python扩展,在Windows下可以使用VC来编译Python扩展。C/C++扩展流程如下:
(1)设置编译环境:
VC6.0下,打开tools->options->directories->show directories for,将Python安装目录下的inlude目录添加到inlude files项中,将libs目录添加到library files项中。
VC2005下,打开tools->options->项目和解决方案->VC++目录,然后做相同工作。
(2)创建空的Win32 Dynamic-Link Library工程,添加新的source file,编写代码。
(3)打开settings for改为Win32 Release,单击Link标签,将Release/**.dll改为Release/**.pyd;单击C/C++标签,选择Category->Code Generation,选择Use run-time library中的Multithreaded DLL项。
(4)单击Build->Batch Build,去掉**-Win32 Debug单选框,然后点击Build。
(5)调用该模块:import **。
注意:Python的安装程序中不包含debug版的库文件,不能生成debug版的Python扩展。
下面具体讲述第(2)步中如何编写扩展代码:
(1)头文件
因为Python含有一些预处理定义,所以必须在所有非标准头文件导入之前导入Python.h 。Python.h中所有用户可见的符号都有 Py 或 PY 的前缀,除非定义在标准头文件中。为了方便,“Python.h” 也包含了一些常用的标准头文件,包括<stdio.h>,<string.h>,<errno.h>,<stdlib.h>。如果你的系统没有后面的头文件,则会直接定义函数 malloc() 、 free() 和 realloc() 。
(2)初始化函数
PyMODINIT_FUNC init**()
{
PyObject *mod;
mod = Py_InitModule(“**”, **Methods);
}
该初始化函数必须存在,用于python解释器对模块进行正确的初始化,并作为DLL文件的导出函数。Py_InitModule函数原型为:PyObject* Py_InitModule(char *name, PyMethodDef *methods),methods为方法列表,该函数返回一个指针指向刚创建的模块对象。他是有可能发生严重错误的,也有可能在无法正确初始化时返回NULL。
(3)方法列表
static PyMethodDef **Methods[] =
{
{“FuncName”, Func, METH_VARARGS, “…………..”},
{NULL, NULL, 0, NULL}
};
方法列表中包含Python扩展中的所有可以调用的函数方法,应该被声明为“static PyMethodDef”。每个函数对应的括号里包括函数名、函数、调用方法和描述。其中调用方法应该为METH_VARARGS或者METH_VARARGS|METH_KEYWORDS或者0(代表使用PyArg_ParseTuple()的陈旧变量)。方法列表应该由{NULL, NULL, 0, NULL}作为结束。
(3)函数实现
PyObject* Func(PyObject * self, PyObject *args)
{
…………………..
}
所有函数都应该被声明为PyObject*的类型,每个函数含有两个PyObject*型的参数,参数self只有在函数为Python的内置方法时才被使用,否则为空指针;args为向方法传递的参数,根据方法列表中调用方法的不同而依次使用PyArg_ParseTuple和PyArg_ParseTupleAndKeywords函数处理参数。PyArg_ParseTuple函数原型如下:
int PyArg_ParseTuple(PyObject *args, const char *format, …………..);
函数PyArg_ParseTuple()检查参数类型并转换成C值。它使用模板字符串检测需要的参数类型,正常返回非零,并已经按照提供的地址存入了各个变量值,如果出错(零)则应该让函数返回NULL以通知解释器出错。该函数使用&向后面的可变参数传递值;format参数指定了后面的参数类型,如”iss”表示后面的参数类型分别为int、char*、char*。常见参数类型字符如下:
s char*
s# char*, int
z char* //can be null
z# char*, int //char* can be null
i int
l long int
c char
f float
d double
函数必须返回一个PY对象,这可以通过Py_BuildValue()来完成,其形式与PyArg_ParseTuple()很像,获取格式字符串和C值,并返回新的Python对象。
代码示例:
#include <python.h>
#include <windows.h>
static PyObject *show(PyObject *self, PyObject *args)
{
char *message;
const char *title = NULL;
HWND hwnd = NULL;
int r;
if(!PyArg_ParseTuple(args, "iss", &hwnd, &message, &title))
return NULL;
r = MessageBox(hwnd, message, title, MB_OK);
return Py_BuildValue("i", r);
}
static PyMethodDef myextMethods[] = {
{"show", show, METH_VARARGS, "show a messagebox"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initmyext(void)
{
PyObject *mod;
mod = Py_InitModule("myext", myextMethods);
}
注意:在Python3.1环境下该代码出现问题,后查找原因发现初始化函数已经变为PyInit_**(工程名字、初始化名字和生成的pyd名字必须一样),而Py_InitModule也变为PyModule_Create了。具体待查。