博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python与c互相调用
阅读量:5226 次
发布时间:2019-06-14

本文共 5134 字,大约阅读时间需要 17 分钟。

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

 

准备工作

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

 

内嵌python虚拟机

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

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

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

 

调用python脚本

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

  1、使用PyImport_ImportModule导入脚步模块;

  2、使用PyObject_GetAttrString获取模块特定方法信息;

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

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

  5、使用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
(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
(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
(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 pycallc4 5 def test():6     print 'in python : ', pycallc.py_get_data()7     pycallc.py_set_data("change hello world!")

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

  上面完整的代码demo的链接:

 

总结

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

 

参考

  python官方:

转载于:https://www.cnblogs.com/chengxuyuancc/p/5355202.html

你可能感兴趣的文章
PHP中file_get_contents函数获取带BOM的utf-8,然后json_decode() 返回null的问题
查看>>
SQLServer代理新建或者编辑作业报错
查看>>
LeetCode 搜索二维矩阵 II
查看>>
Python升级3.多
查看>>
算术表达式解析(第一版)
查看>>
java.lang.ClassNotFoundException: org.hibernate.annotations.common.reflection.MetadataProvider
查看>>
兼容各种浏览器的透明层效果
查看>>
软件工程概论课总结
查看>>
UVA11255 Necklace Burnside、组合
查看>>
HDU 2859 Phalanx
查看>>
Docker生产实践(六)
查看>>
机器学习实战5-AdaBoost
查看>>
web-11. 层叠式表的属性与滤镜
查看>>
Vue
查看>>
表变量与临时表的优缺点(转)
查看>>
shell脚本图书
查看>>
UNIX环境高级编程——线程限制
查看>>
UNIX网络编程——原始套接字SOCK_RAW
查看>>
TCP发送源码学习(1)--tcp_sendmsg
查看>>
使用两个不同类型的数据进行加法计算时,使用异常处理语句捕获由于数据类型错误而出现的异常,发生生成错误。是否继续并运行上次的成功生成?...
查看>>