python与c互相调用

  虽然python开发效率很高,但做为脚本语言,其性能不高,因此为了兼顾开发效率和性能,一般把性能要求高的模块用c或c++来实现或者在c或c++中运行python脚原本处理逻辑,前者一般是python中一些模块的实现方式,后者服务端程序(实现业务扩展或是Plugin功能)和游戏开发(脚本只处理逻辑)中比较常见。本文主要介绍经过在c中运行python脚原本实现python与c的相互调用,并经过c和python脚本设置同一段内存区域为例子来说解。html

 

准备工做python

  为了在c中运行python脚本,须要在程序连接的时候将python虚拟机库连接进去,python虚拟机库是python安装目录下libs中的python27.lib文件,至于怎样将库连接进程序中能够本身google下。因为在c中使用了python的一些方法和数据结构,因此须要将python安装目录下的include目录添加到项目include目录中。好了,须要准备的就是这些,而后就能够开始实现一个设置内存区域的例子了。c++

 

内嵌python虚拟机git

  在c中内嵌python虚拟机很简单,只须要在程序开头include Python.h头文件,而后调用下面两段来初始化python虚拟机实例就好了。github

1 Py_SetPythonHome("D:\Python27");
2 Py_Initialize();

  Py_SetPythonHome函数是用来设置python的库路径,也就是python安装路径,Py_Initialize函数真正实例化一个python虚拟机,这样就把一个python虚拟机内嵌到c中了。api

 

调用python脚本数据结构

   将python虚拟机初始化后,其实就能够调用python脚本了。c中调用脚本模块中的方法分下面几个步骤:ide

  一、使用PyImport_ImportModule导入脚步模块;
函数

  二、使用PyObject_GetAttrString获取模块特定方法信息;性能

  三、使用Py_VaBuildValue转换输入参数;

  四、使用PyObject_CallObject调用特定方法;

  五、使用PyArg_Parse转换方法的返回结果。

  因为上面流程在调用模块中的方法都是必须的,因此能够写个函数来封装上面的5个步骤,具体代码以下:

 1 int PyModuleRunFunction(const char *module, const char *function,
 2                         const char *result_format, void *result, const char *args_format, ...)
 3 {
 4 
 5     PyObject *pmodule, *pfunction, *args, *presult;
 6 
 7     pmodule = PyImport_ImportModule(const_cast<char *>(module));
 8     if (!pmodule)
 9     {
10         PyObject *type = PyErr_Occurred();
11         if (type == PyExc_NameError)
12         {
13             PyErr_Clear();
14             return 0;
15         }
16 
17         PyError("PyModuleRunFunction");
18         return -1;
19     }
20 
21     pfunction = PyObject_GetAttrString(pmodule, const_cast<char *>(function));
22     Py_DECREF(pmodule);
23     if (!pfunction)
24     {
25         PyObject *type = PyErr_Occurred();
26         if (type == PyExc_AttributeError)
27         {
28             PyErr_Clear();
29             return 0;
30         }
31 
32         PyError("PyModuleRunFunction");
33         return -2;
34     }
35 
36     if (pfunction == Py_None)
37     {
38         return 0;
39     }
40 
41     va_list args_list;
42     va_start(args_list, args_format);
43     args = Py_VaBuildValue(const_cast<char *>(args_format), args_list);
44     va_end(args_list);
45 
46     if (!args)
47     {
48         Py_DECREF(pfunction);
49         return -3;
50     }
51 
52     presult = PyObject_CallObject(pfunction, args);
53     if (presult == 0)
54     {
55         PyError("PyModuleRunFunction");
56         Py_XDECREF(pfunction);
57         Py_XDECREF(args);
58         return -1;
59     }
60 
61     Py_XDECREF(pfunction);
62     Py_XDECREF(args);
63 
64     return ConvertResult(presult, result_format, result);
65 }
View Code

  有了上面的调用python模块内方法的通用函数,咱们就能够直接调用python脚本中的方法了,具体以下:

1 PyModuleRunFunction("hello", "test", "", 0, "()");

   这样咱们就实现了再c中调用python的方法。下面咱们再来开心python怎么调用c中的方法。

 

初始化c实现的python模块

   为了能在python脚本中调用到c中定义的方法,须要先在c中定义一个python模块,而后在脚本中import这个模块,最后经过这个模块来间接调用c中定义的方法。例如,咱们经过c定义了一块内存区域data和对这个内存区域操做的函数SetData与GetData(代码以下),怎样在脚本中调用SetData与GetData函数来操做data呢?其实关键问题是怎么样在脚本中调用SetData和GetData函数,若是能在脚本中调用这两个函数,天然就能操做data了。python中经过模块的方式来解决这个问题。

 1 #define min(a,b)    (((a) < (b)) ? (a) : (b))
 2 
 3 char data[1024];
 4 
 5 void SetData(const char *str)
 6 {
 7     strncpy(data, str, min(strlen(str) + 1, 1024));
 8 }
 9 
10 const char *GetData()
11 {
12     return data;
13 }

  在c中定义一个python模块有特定的步骤,具体代码以下:

 1 PyDoc_STRVAR(PySetData_doc__, "\
 2 测试\n\
 3 \n\
 4 PySetData(str)\n\
 5 str: 出入的字符串\n\
 6 返回: \n\
 7 null \n\
 8 ");
 9 static PyObject* PySetData(PyObject *self, PyObject *args)
10 {
11     const char* str = NULL;
12     if ( !PyArg_ParseTuple(args, "s", &str) )
13     {
14         return 0;
15     }
16     SetData(str);
17     Py_RETURN_NONE;
18 }
19 
20 PyDoc_STRVAR(PyGetData_doc__, "\
21 打印数据\n\
22 \n\
23 PyGetData()\n\
24 返回: \n\
25 data \n\
26 ");
27 static PyObject* PyGetData(PyObject *self, PyObject *args)
28 {
29     const char* str = NULL;
30     return PyString_FromString(GetData());
31 }
32 
33 static PyMethodDef module_methods[] = {
34     {"py_set_data", PySetData, METH_VARARGS, PySetData_doc__},
35     {"py_get_data", PyGetData, METH_VARARGS, PyGetData_doc__},
36     {NULL}
37     };
38 void InitCCallPy()
39 {
40     PyObject *module = Py_InitModule3("pycallc", module_methods,
41         "python call c");
42 }
View Code

  Py_InitModule3用来定义一个python模块,第一个参数是模块的名字,第二个参数是模块中的方法描述集合,第三个参数是模块的描述信息。上面代码中咱们定义了一个叫pycallc的模块,方法描述集合module_methods描述了两个方法py_set_data和py_get_data,这两个方法对应的函数地址是PySetData和PyGetData,这两个函数最终会分别调用前面定义的SetData和GetData。这样咱们在python脚本中经过pycallc模块的py_set_data和py_get_data方法就能够设置和获取data数据了。看了上面的实现,其实这个python模块的主要做用就是把c中定义的函数再封装一次,封装的函数可以被python识别。

 

在python脚本中调用c实现的python模块

   因为前面已经经过c代码初始化了一个python模块pycallc,那么在脚本中咱们就能够经过import导入这个模块,并调用这个模块中的函数。具体代码以下:

1 # -*- coding: utf-8 -*-
2 
3 import pycallc
4 
5 def test():
6     print 'in python : ', pycallc.py_get_data()
7     pycallc.py_set_data("change hello world!")

   这样咱们就实现了在python脚本中调用c中的方法。

  上面完整的代码demo的连接:https://github.com/morningstatus/python/tree/master/ccallpy

 

总结

  从上面c调用python,python调用c,其实都是一些固定的步骤,知道就会用了,没有会不会的问题,只有想不想知道的问题。没有接触这个技术前可能以为它很高深,但其实只要稍微花点心思去了解它,它也其实没有这么难。计算机不少技术不外乎都是这样,只有你想不想的问题,没有你会不会的问题,多问,多思考,多学习,总有一天你也能成为技术大牛。

 

参考

  python官方:https://docs.python.org/2/c-api/index.html

相关文章
相关标签/搜索