虽然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虚拟机实例就行了。

 Py_SetPythonHome("D:\Python27");
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个步骤,具体代码如下:

 int PyModuleRunFunction(const char *module, const char *function,
const char *result_format, void *result, const char *args_format, ...)
{ PyObject *pmodule, *pfunction, *args, *presult; pmodule = PyImport_ImportModule(const_cast<char *>(module));
if (!pmodule)
{
PyObject *type = PyErr_Occurred();
if (type == PyExc_NameError)
{
PyErr_Clear();
return ;
} PyError("PyModuleRunFunction");
return -;
} pfunction = PyObject_GetAttrString(pmodule, const_cast<char *>(function));
Py_DECREF(pmodule);
if (!pfunction)
{
PyObject *type = PyErr_Occurred();
if (type == PyExc_AttributeError)
{
PyErr_Clear();
return ;
} PyError("PyModuleRunFunction");
return -;
} if (pfunction == Py_None)
{
return ;
} va_list args_list;
va_start(args_list, args_format);
args = Py_VaBuildValue(const_cast<char *>(args_format), args_list);
va_end(args_list); if (!args)
{
Py_DECREF(pfunction);
return -;
} presult = PyObject_CallObject(pfunction, args);
if (presult == )
{
PyError("PyModuleRunFunction");
Py_XDECREF(pfunction);
Py_XDECREF(args);
return -;
} Py_XDECREF(pfunction);
Py_XDECREF(args); return ConvertResult(presult, result_format, result);
}

  有了上面的调用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中通过模块的方式来解决这个问题。

 #define min(a,b)    (((a) < (b)) ? (a) : (b))

 char data[];

 void SetData(const char *str)
{
strncpy(data, str, min(strlen(str) + , ));
} const char *GetData()
{
return data;
}

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

 PyDoc_STRVAR(PySetData_doc__, "\
测试\n\
\n\
PySetData(str)\n\
str: 出入的字符串\n\
返回: \n\
null \n\
");
static PyObject* PySetData(PyObject *self, PyObject *args)
{
const char* str = NULL;
if ( !PyArg_ParseTuple(args, "s", &str) )
{
return ;
}
SetData(str);
Py_RETURN_NONE;
} PyDoc_STRVAR(PyGetData_doc__, "\
打印数据\n\
\n\
PyGetData()\n\
返回: \n\
data \n\
");
static PyObject* PyGetData(PyObject *self, PyObject *args)
{
const char* str = NULL;
return PyString_FromString(GetData());
} static PyMethodDef module_methods[] = {
{"py_set_data", PySetData, METH_VARARGS, PySetData_doc__},
{"py_get_data", PyGetData, METH_VARARGS, PyGetData_doc__},
{NULL}
};
void InitCCallPy()
{
PyObject *module = Py_InitModule3("pycallc", module_methods,
"python call c");
}

  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导入这个模块,并调用这个模块中的函数。具体代码如下:

 # -*- coding: utf-8 -*-

 import pycallc

 def test():
print 'in python : ', pycallc.py_get_data()
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

python与c互相调用的更多相关文章

  1. [Python陷阱]os.system调用shell脚本获取返回值

    当前有shell个脚本/tmp/test.sh,内容如下: #!/bin/bashexit 11 使用Python的os.system调用,获取返回值是: >>> ret=os.sy ...

  2. python中使用ctypes调用MinGW生成的动态链接库(dll)

    关于gcc编译dll的我就不说了,网上举例一大堆,下面以g++为例. 假设有一个test.cpp文件如下: extern "C" { __declspec(dllexport) d ...

  3. 判断python对象是否可调用的三种方式及其区别

    查找资料,基本上判断python对象是否为可调用的函数,有三种方法 使用内置的callable函数 callable(func) 用于检查对象是否可调用,返回True也可能调用失败,但是返回False ...

  4. Python 的 JPype 模块调用 Jar 包

    背景与需求 最近学习并安装使用了HttpRunner框架去尝试做接口测试,并有后续在公司推广的打算. HttpRunner由Python开发,调用接口时需要依赖Python:而大多数公司的扩展工具包使 ...

  5. python基础--------字符串的调用详解(2)

    Python 字符串的的调用方法~~~@@@ 17.  strip  : 去除字符串左右两边指定的字符 18.   rstrip : 去除字符串右边指定的字符 19 .   lstrip  :  去除 ...

  6. python 基础 ------字符串的调用详解(1)

    Python 字符串的的调用方法~~~ 废话不多说直接奔主题 >>>>>>>>>>>>>>>>> ...

  7. Python装饰器的调用过程

    在Python学习的过程中,装饰器是比较难理解的一个应用.本人也在学习期间也遇到很多坑,现将装饰器的基本调用过程总结一下. 首先,装饰器用到了“闭包”,而“闭包”是学习装饰器的基础,所以在讲装饰器之前 ...

  8. Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)

    Python 在子类中调用父类方法详解(单继承.多层继承.多重继承)   by:授客 QQ:1033553122   测试环境: win7 64位 Python版本:Python 3.3.5 代码实践 ...

  9. (转载)Python 的 JPype 模块调用 Jar 包

    Python 的 JPype 模块调用 Jar 包 背景与需求 最近学习并安装使用了HttpRunner框架去尝试做接口测试,并有后续在公司推广的打算. HttpRunner由Python开发,调用接 ...

  10. [笔记]Python中模块互相调用的例子

    python中模块互相调用容易出错,经常是在本地路径下工作正常,切换到其他路径来调用,就各种模块找不到了. 解决方法是通过__file__定位当前文件的真实路径,再通过sys.path.append( ...

随机推荐

  1. 消息队列——RabbitMQ学习笔记

    消息队列--RabbitMQ学习笔记 1. 写在前面 昨天简单学习了一个消息队列项目--RabbitMQ,今天趁热打铁,将学到的东西记录下来. 学习的资料主要是官网给出的6个基本的消息发送/接收模型, ...

  2. Npm包的开发

    个人开发包的目录结构 ├── coverage //istanbul测试覆盖率生成的文件 ├── index.js //入口文件 ├── introduce.md //说明文件 ├── lib │   ...

  3. 玩转spring boot——快速开始

    开发环境: IED环境:Eclipse JDK版本:1.8 maven版本:3.3.9 一.创建一个spring boot的mcv web应用程序 打开Eclipse,新建Maven项目 选择quic ...

  4. .NET设计模式访问者模式

    一.访问者模式的定义: 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 二.访问者模式的结构和角色: 1.Visitor 抽象访问者角色,为该 ...

  5. H3 BPM让天下没有难用的流程之功能介绍

    H3 BPM10.0功能地图如下:  图:H3 BPM 功能地图 一.流程引擎 H3  BPM 流程引擎遵循WFMC 标准的工作流引擎技术,设计可运行的流程和表单,实现工作任务在人与人.人与系统.系统 ...

  6. 拦截UIViewController的popViewController事件

    实现拦截UIViewController的pop操作有两种方式: 自定义实现返回按钮,即设置UIBarButtonItem来实现自定义的返回操作. 创建UINavigatonController的Ca ...

  7. 浅谈iptables 入站 出站以及NAT实例

    --------------本文是自己工作上的笔记总结,适合的可以直接拿去用,不适合的,适当修改即可!--------------- iptbales默认ACCEPT策略,也称通策略,这种情况下可以做 ...

  8. Johnson 全源最短路径算法

    解决单源最短路径问题(Single Source Shortest Paths Problem)的算法包括: Dijkstra 单源最短路径算法:时间复杂度为 O(E + VlogV),要求权值非负: ...

  9. Guava库介绍之实用工具类

    作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文是我写的Google开源的Java编程库Guava系列之一,主要介 ...

  10. 浅析Java 泛型

    泛型是JavaSE5引入的一个新概念,但是这个概念在编程语言中却是很普遍的一个概念.下面,根据以下内容,我们总结下在Java中使用泛型. 泛型使用的意义 什么是泛型 泛型类 泛型方法 泛型接口 泛型擦 ...