zoukankan      html  css  js  c++  java
  • Python:C语言扩展

    1. 概述

    Python 可以非常方便地和 C 进行相互的调用。

    一般,我们不会使用 C 去直接编写一个 Python 的模块。通常的情景是,我们需要把 C 的相关模块包装一下,然后在 Python 中可以直接调用它。或者是,把 Python 逻辑中的某一效率要求很高的部分使用 C 来实现。整个过程大概是:

    1. 引入 Python.h 头文件。
    2. 编写包装函数。
    3. 函数中处理从 Python 传入的参数。
    4. 实现功能逻辑。
    5. 处理 C 中的返回值,包装成 Python 对象。
    6. 在一个 PyMethodDef 结构体中注册需要的函数。
    7. 在一个初始化方法中注册模块名。
    8. 把这个 C 源文件编译成链接库。
     1   int add(int x, int y){
     2       return x + y;
     3   }
     4   
     5   //int main(void){
     6   //    printf("%d", add(1, 2));
     7   //    return 0;
     8   //}
     9   
    10   #include<Python.h>
    11   
    12   static PyObject* W_add(PyObject* self, PyObject* args){
    13       int x;
    14       int y;
    15       if(!PyArg_ParseTuple(args, "i|i", &x, &y)){
    16           return NULL;
    17       } else {
    18           return Py_BuildValue("i", add(x, y));
    19       }
    20   }
    21   
    22   static PyMethodDef ExtendMethods[] = {
    23       {"add", W_add, METH_VARARGS, "a function from C"},
    24       {NULL, NULL, 0, NULL},
    25   };
    26   
    27   PyMODINIT_FUNC initdemo(){
    28       Py_InitModule("demo", ExtendMethods);
    29   }

    2. 引入 Python.h 头文件

    这个文件一般位于 Python 的主目录中。比如我的 Ubuntu 10.04 下,它的位置在:

    1   /usr/include/python2.6

     在最后编译的时候指定目录就可以了。

    3. 编写包装函数

    因为 Python 用到的函数与普通的 C 函数,在输入和输出上,会有一些不同,所以,我们需要把普通的 C 做一些封来给 Python 用。

    从另一方面来说,在实现功能的过程中,我们可以先完全不考虑这东西是拿给 Python 用的,只专注于使用 C 把它写好就可以了。最后,功能写好,测试没有问题之后,再做 Python 封装的工作。

    包装函数一般声明成 static ,并且第一个参数是一个默认传入的 Python 对象,就是 Python 中某个对象的属性方法一样,第二个参数才是我们调用时传入的参数(实际上它是一个序列化后的字符串):

    1   static PyObject* W_add(PyObject* self, PyObject* args);

    4. 处理从 Python 传入的参数

    因为我们的相关函数,之后是在 Python 环境中被调用的,那么它显然接受的就是从 Python 环境下传入的参数。这和 C 中你看到的函数是不同的,在 Python 的世界中,一切都是对象。所以,包装函数中首先要处理的问题就是解析从 Python 占获取的参数。

    常用的函数有: PyArg_ParseTuple

    1   int x;
    2   int y;
    3   PyArg_ParseTuple(args, "i|i", &x, &y);

    PyArg_ParseTuple 的作用是解析我们从 Python 中传入的 args 这个字符串,然后以我们规定的格式将解析结果放入指定变量的内存位。

    " i|i " 就表示要把传入的东西解析成两个整数,同样,还有 s 表示字符串等。

    5. 实现逻辑功能

    这部分没什么特别的,只需要在 C 中一样调用函数就可以了,相关变量我们已经在上一步处理过了。

      add(x, y);

    6. 处理 C 中的返回值

    我们使用 C 完成了功能逻辑, C 中会产生一个返回值,要将这个值返回到我们之前调用函数的 Python 环境中,当然还需要经过一些处理才行。

    常用的函数是: Py_BuildValue

      return Py_BuildValue("i", add(x, y));

    这个函数的用法和上一步中的 PyArg_ParseTuple 是一样的,它们过程相反。 Py_BuildValue 把 C 中的值按给定的格式格式化成 Python 需要的对象。这里注意一下,对于 W_add 这个函数,我们可是声明了它的返回类型为 PyObject* 的哦。

    7. 注册函数

    在上面的实现完成之后,就需要作导出的准备了。第一步,就是要在一个类型为 PyMethodDef 的结构体中注册我们需要导出到 Python 中的函数:

    1   static PyMethodDef ExtendMethods[] = {
    2       {"add", W_add, METH_VARARGS, "a function from C"},
    3       {NULL, NULL, 0, NULL},
    4   }

    这个结构体成员有四个函数:

    1. " add " 导出后在 Pyhton 中可见的方法名。
    2. W_add 实际映射到 C 中的方法名。
    3. METH_VARARGS 表示传入方法的是普通参数,当然还可以处理关键词参数。
    4. 此方法的注释。

    8. 注册模块

    在注册了方法后,就要注册此模块了。方法是定义一个 init* 的函数:

    1   PyMODINIT_FUNC initdemo(){
    2       Py_InitModule("demo", ExtendMethods);
    3   }

     方法名必须是 init 加上模块名,然后调用 Py_InitModule 来注册模块,这个函数的第一个参数就是模块名,第二个参数是此模块中我们导出的方法,就是上一步我们定义的结构体。

    9. 编译

    最后一步就是编译了。没什么特别的,指定好 Python.h 头文件的位置就可以了:

      gcc demo.c -I /usr/include/python2.6 -shared -o demo.so

    当然,链接库的名字要和我们期望导出的模块名一致。

    这样,你就可以在 Python 中使用 import 直接引入 demo 模块,然后调用它的 add 方法了:

      import demo
      demo.add(3, 4)

      

    Sample

      1 #include <string>
      2 #include <iostream>
      3 #include <Python.h>
      4  
      5 //Python c api使用方法
      6   
      7 using namespace std;
      8  
      9 string GetPyFun(string s1,string s2)
     10 {
     11    // void Py_Initialize( )
     12    //初始化Python解释器,在C++程序中使用其它Python/C API之前,必须调用此函数,如果调用失败,将产生一个致命的错误
     13    Py_Initialize();
     14 
     15    //定义变量
     16    PyObject * pModule = NULL;
     17    PyObject * pFunc = NULL;
     18    PyObject * pArg    = NULL;
     19    PyObject * result;
     20    char *resultStr = "";
     21 
     22     //int PyRun_SimpleString( const char *command)
     23     //直接执行一段Python代码,就好象是在__main__ 函数里面执行一样。
     24     //PyRun_SimpleString("import sys");
     25    //PyRun_SimpleString("sys.path.append('C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\hello\hello')");
     26 
     27    //PyObject* PyImport_ImportModule(char *name)
     28    //导入一个Python模块,参数name可以是*.py文件的文件名。相当于Python内建函数__import__()
     29    pModule =PyImport_ImportModule("hello");//这里是要调用的文件名
     30 
     31    //PyObject* PyObject_GetAttrString(PyObject *o, char *attr_name)
     32    //返回模块对象o中的attr_name属性或函数,相当于Python中表达式语句:o.attr_name
     33    pFunc= PyObject_GetAttrString(pModule, "Hello");
     34  
     35    //PyObject* Py_BuildValue( char *format, ...)
     36    //format以tuple的形式指定,一个参数就是(i)
     37    //构建一个参数列表,把C类型转换为Python对象,使Python可以使用C类型数据
     38    pArg= Py_BuildValue("(s,s)", s1.c_str(),s2.c_str());
     39 
     40    //pParm = PyTuple_New(2);
     41    //PyTuple_SetItem(pParm, 0, Py_BuildValue("s", csEntity));
     42    //PyTuple_SetItem(pParm, 1, Py_BuildValue("s", csEntity));
     43 
     44    //PyObject* PyEval_CallObject(PyObject* pfunc, PyObject* pargs)
     45    //用于调用Python函数
     46    //此函数接受两个PyObject*形参
     47    //pfunc是要被调用的Python函数,通常可由PyObject_GetAttrString获得
     48    //pargs是函数的参数列表,通常可由Py_BuildValue获得
     49    result = PyEval_CallObject(pFunc, pArg);
     50  
     51     //int PyArg_Parse( PyObject *args, char *format, ...)
     52     //解构Python数据为C的类型,这样C程序中才可以使用Python里的数据。
     53     PyArg_Parse(result, "s", &resultStr);
     54 
     55     //关闭Python解释器,释放解释器所占用的资源
     56    Py_Finalize();
     57    return resultStr;
     58    }
     59 
     60    int Walk(const string& s1,const string& s2)
     61    {
     62     // void Py_Initialize( )
     63     //初始化Python解释器,在C++程序中使用其它Python/C API之前,必须调用此函数,如果调用失败,将产生一个致命的错误
     64    Py_Initialize();
     65    
     66    //定义变量
     67    PyObject * pModule = NULL;
     68    PyObject * pFunc = NULL;
     69    PyObject * pArg    = NULL;
     70    PyObject * result;
     71    int reVal = 0;
     72 
     73    //int PyRun_SimpleString( const char *command)
     74    //直接执行一段Python代码,就好象是在__main__ 函数里面执行一样。
     75    //PyRun_SimpleString("import sys");
     76    //PyRun_SimpleString("sys.path.append('C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\hello\hello')");
     77 
     78    //PyObject* PyImport_ImportModule(char *name)
     79    //导入一个Python模块,参数name可以是*.py文件的文件名。相当于Python内建函数__import__()
     80    pModule =PyImport_ImportModule("walkdir");//这里是要调用的文件名
     81    
     82    //PyObject* PyObject_GetAttrString(PyObject *o, char *attr_name)
     83    //返回模块对象o中的attr_name属性或函数,相当于Python中表达式语句:o.attr_name
     84    pFunc= PyObject_GetAttrString(pModule, "list_dir");
     85    
     86    //PyObject* Py_BuildValue( char *format, ...)
     87    //format以tuple的形式指定,一个参数就是(i)
     88    //构建一个参数列表,把C类型转换为Python对象,使Python可以使用C类型数据
     89    pArg= Py_BuildValue("(s,s)", s1.c_str(),s2.c_str());
     90 
     91    //pParm = PyTuple_New(2);
     92    //PyTuple_SetItem(pParm, 0, Py_BuildValue("s", csEntity));
     93    //PyTuple_SetItem(pParm, 1, Py_BuildValue("s", csEntity));
     94 
     95    //PyObject* PyEval_CallObject(PyObject* pfunc, PyObject* pargs)
     96    //用于调用Python函数
     97    //此函数接受两个PyObject*形参
     98    //pfunc是要被调用的Python函数,通常可由PyObject_GetAttrString获得
     99    //pargs是函数的参数列表,通常可由Py_BuildValue获得
    100    result = PyEval_CallObject(pFunc, pArg);
    101 
    102    //int PyArg_Parse( PyObject *args, char *format, ...)
    103    //解构Python数据为C的类型,这样C程序中才可以使用Python里的数据。
    104    PyArg_Parse(result, "i", &reVal);
    105   
    106    //关闭Python解释器,释放解释器所占用的资源
    107    Py_Finalize();
    108    return reVal;
    109   }
    110 
    111    int main()
    112   {
    113    //string re=GetPyFun("hello","world");
    114    //cout<<"
    "<<re<<endl;
    115 
    116      cout<<"enter a path and a filename:"<<endl;
    117      string path,dir;
    118      cin>>path>>dir;
    119      int re=Walk(path,dir);
    120  
    121      return 0;
    122   }
  • 相关阅读:
    植物 miRNA 研究
    TargetScan 数据库简介
    miRTarBase 数据库简介
    dendrogram 和 barplot 的组合
    多线程基础必要知识点!看了学习多线程事半功倍
    Thread源码剖析
    多线程三分钟就可以入个门了!
    Java集合总结【面试题+脑图】,将知识点一网打尽!
    3分钟搞掂Set集合
    TreeMap就这么简单【源码剖析】
  • 原文地址:https://www.cnblogs.com/yinguo/p/4641349.html
Copyright © 2011-2022 走看看