在windows上扩展python
在windows环境下建立一个python的模块。 使用vc6和python2.3
步骤:
1. 在vc6中建立一个dll的project。 假定module的名字是mytest.那么dll名字就是mytest.dll。 这个是必须的。
2. 用c写要实现的部分。步骤如下:
#include
这个是必须的,python的类型都在这里定义(类似写window程序一定要包含windows.h一样)。所以,在vc里一定要设置好include的路径。这个文件在 Python23\include\下面。
3. 写要实现的函数。比如要实现两个c的函数,strlen和strcat。必须分别包装它们,命名为my_strlen和my_strcat。
先声明:
static PyObject* my_strlen(PyObject *self, PyObject *args);
static PyObject* my_strcat(PyObject *self, PyObject *args);
一定声明为static,把它们限制在这个文件范围里。
几乎所有的参数都是PyObject类型。 在python,每个东西都是object。
这两个函数的定义是这样的:
static PyObject* my_strlen(PyObject *self, PyObject *args)
{
char *string;
int len;
if (!PyArg_ParseTuple(args, "s", &string))
return NULL;
len = strlen(string);
return Py_BuildValue("i", len);
}
static PyObject* my_strcat(PyObject *self, PyObject *args)
{
char* string1;
char* string2;
char* newstring;
if (!PyArg_ParseTuple(args, "s|s", &string1, &string2))
return NULL;
newstring = strcat(string1, string2);
return Py_BuildValue("s", newstring);
}
第一个参数是self,这个是python用的, 每个函数都要有。args是一个参数列表。它把所有的参数都整合成一个string。所以需要从这个string里来解析参数。
PyArg_ParseTuple来完成这个任务。第一个参数是args, 就是要转换的参数。第二个是格式符号。“s”代表是个string。 从args里提取一个参数就写"s", 两个的话就写"s|s", 如果是一个string,一个int,就写"s|i", 和printf差不多。第三个参数就是提取出来的参数放置的真正位置。必须传递这个参数的地址。对于my_strcat,它将提取两个参数。分别是string1和string2。
然后调用真正的实现。分别是strlen和strcat。
调用完之后需要返回结果。这个结果是c的type或者是自定义的类型。必须把它转换成PyObject,这个用Py_BuildValue来完成。它是PyArg_ParseTuple的逆过程。它的第一个参数和PyArg_ParseTuple的第二个参数一样, 是个格式化符号。第三个参数是需要转换的参数。Py_BuildValue会把所有的返回只组装成一个tutple给python。
实际上,这样只是把要实现的部分调用python提供的api来封装了一下。但是python并不知道怎么用这些函数。还需要做一些工作。
static PyMethodDef mytestMethods[] =
{
{"mystrlen", my_strlen, METH_VARARGS, "We test strlen of C"},
{"mystrcat", my_strcat, METH_VARARGS, "We test strcat of C"},
{NULL, NULL, 0, NULL}
};
这个是一个c的结构。它来完成一个映射。需要把扩展的函数都映射到这个表里。表的第一个字段是python真正认识的。是python里的方法名字。 第二个字段是python里的这个方法名字的具体实现的函数名。 在python里调用mystrlen, 真正执行的是用c写的my_strlen函数。第三个字段是METH_VARARGS, 它告诉python,mystrlen是调用c函数来实现的。第四个字段是这个函数的说明。如果在python里来help这个函数,将显示这个说明。相当于在python里的函数的文档说明。
这样就建立了这个映射表。这个表是在python导入这个module的时候来用的。但是python 是如何来调用这个表的呢。通过这个函数
_declspec(dllexport) void initmytest()
{
(void) Py_InitModule("mytest", mytestMethods);
}
注意,这个函数的名字不能改动。 必须是init+模块名字。 模块名字是mytest。所以这个函数是initmytest()。 这个函数应该被导出。所以使用 _declspec(dllexport)。 这样python在导入mytest 的模块时候,才会找到这个函数,并调用。这个函数调用Py_InitModule来将模块名字和映射表结合在一起。 他表示,mytest这个模块使用 mytestMethods这个映射表。python应该这样导入module的:
a. 当import mytest的时候。 python装载这个module的dll。 在这里是mytest.dll
b. 然后在这个dll里调用, initmytest函数来建立一个映射表。 如果这个initmytest函数不实现或者没有导出。就不能成功把这个module导入到python里。
c. python 从映射表知道这个模块实现了几个方法。并且名字分别是什么。当调用他们的时候,找到相应的c的函数。
写完这些代码以后,可以在vc6里编译这个dll, 注意,必须编译成release版本。并且链接python23.lib。 如果是debug版本它将会找python23_d.lib。编译的dll名字是mytest.dll。如果不是的话,在vc里改动link的设置。
把mytest.dll拷到python23的lib目录下,或者python能搜索到的路径下。 进入python的shell
>>import mytest.dll
>>mytest.mystrlen("123")
3
>>mytest.mystrcat("123", "456")
123456
#include Python.h>
static PyObject* my_strlen(PyObject *self, PyObject *args);
static PyObject* my_strcat(PyObject *self, PyObject *args);
static PyMethodDef mytestMethods[] =
{
{"mystrlen", my_strlen, METH_VARARGS, "We test strlen of C"},
{"mystrcat", my_strcat, METH_VARARGS, "We test strcat of C"},
{NULL, NULL, 0, NULL}
};
static PyObject* my_strlen(PyObject *self, PyObject *args)
{
char *string;
int len;
if (!PyArg_ParseTuple(args, "s", &string))
return NULL;
len = strlen(string);
return Py_BuildValue("i", len);
}
static PyObject* my_strcat(PyObject *self, PyObject *args)
{
char* string1;
char* string2;
char* newstring;
if (!PyArg_ParseTuple(args, "s|s", &string1, &string2))
return NULL;
newstring = strcat(string1, string2);
return Py_BuildValue("s", newstring);
}
_declspec(dllexport) void initmytest()
{
(void) Py_InitModule("mytest", mytestMethods);
}