• Python 3.1 用C写模块 扩展(序)


    注意。此方式只在win下用vc2008成功。其他编译器/环境未试验:

    1。 在vc中建立一个dll的project。 假定我们的module的名字是mytest.那么我们的dll名字就是mytest.dll。 这个是必须的。
    2。 用c写我们想要实现的部分。步骤如下 

    #include <Python.h> //这个是必须的。python的类型都在这里定义。
    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, 让python认识。这个用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的时候来用的。

    /*下面就和2.x版本的约定不同了*/
    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}
    };
    char name[] = "mytest";
    static PyModuleDef mytestModule;
    
    //对于其他平台。函数的修饰符可能不同
    _declspec(dllexport)void PyInit_mytest()
    {
        mytestModule.m_methods = mytestMethods;//模块的方法表
        mytestModule.m_name = name;//模块说明
        PyModule_Create2(&mytestModule, PYTHON_API_VERSION);
    }

    注意,这个函数的名字不能改动。 必须是PyInit_+模块名字。 我们的模块名字是mytest。所以这个函数是PyInit_mytest()。 这个函数应该被 导出。所以使用 _declspec(dllexport)。 这样python在导入mytest 的模块时候,才会找到这个函数,并调用。

    a. 当我们import mytest的时候。 python装载这个module的dll。 在这里是mytest.dll
    b. 然后在这个dll里调用, PyInit_mytest函数来建立一个映射表。 如果这个initmytest函数不实现或者没有导出。就不能成功把这个module
    导入到python里。
    c. python 从映射表 知道这个模块实现了几个方法。并且名字分别是什么。当调用他们的时候,找到相应的c的函数。

    写完这些代码以后,我们可以在vc里编译这个dll, 注意,必须编译成release版本。编译的dll名字是mytest.dll。如果不是的话,在vc里改动link的设置。 

    把mytest.dll改名为mytest.pyd,然后拷到python3的DLLs目录下

    #test.py 测试代码
    import mytest
    print(mytest.mystrlen("123") )
    print(mytest.mystrcat("123", "456"))

    应该输出:
    3
    123456

  • 相关阅读:
    hdu 1823 Luck and Love 二维线段树
    UVA 12299 RMQ with Shifts 线段树
    HDU 4578 Transformation 线段树
    FZU 2105 Digits Count 线段树
    UVA 1513 Movie collection 树状数组
    UVA 1292 Strategic game 树形DP
    【ACM】hdu_zs2_1003_Problem C_201308031012
    qsort快速排序
    【ACM】nyoj_7_街区最短路径问题_201308051737
    【ACM】nyoj_540_奇怪的排序_201308050951
  • 原文地址:https://www.cnblogs.com/lcinx/p/10570405.html
Copyright © 2020-2023  润新知