如何实现 C/C与 Python 的通信C/C调用 Python(基础篇)
Python 本身就是一个C库 。python与c函数交互你所看到的可执行体python只不过是个stub 。真正的python实体在动态链接库里实现python与c函数交互,在Windows平台上 , 这个文件位于 %SystemRoot%\System32\python27.dll 。
你也可以在自己的程序中调用Python,看起来非常容易:
//my_python.c
【python与c函数交互 c和python交互】#include Python.h
int main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]);
Py_Initialize();
PyRun_SimpleString("print 'Hello Python!'\n");
Py_Finalize();
return 0;
}
在Windows平台下,打开Visual Studio命令提示符,编译命令为
cl my_python.c -IC:\Python27\include C:\Python27\libs\python27.lib
在Linux下编译命令为
gcc my_python.c -o my_python -I/usr/include/python2.7/ -lpython2.7
在Mac OS X 下的编译命令同上
产生可执行文件后,直接运行,结果为输出
Hello Python!
Python库函数PyRun_SimpleString可以执行字符串形式的Python代码 。
虽然非常简单,但这段代码除了能用C语言动态生成一些Python代码之外,并没有什么用处 。我们需要的是C语言的数据结构能够和Python交互 。
下面举个例子,比如说,有一天我们用Python写了一个功能特别强大的函数:
def great_function(a):
return a1
接下来要把它包装成C语言的函数 。我们期待的C语言的对应函数应该是这样的:
int great_function_from_python(int a) {
int res;
// some magic
return res;
}
首先,复用Python模块得做‘import’ , 这里也不例外 。所以我们把great_function放到一个module里,比如说,这个module名字叫 great_module.py
接下来就要用C来调用Python了 , 完整的代码如下:
#include Python.h
int great_function_from_python(int a) {
int res;
PyObject *pModule,*pFunc;
PyObject *pArgs, *pValue;
/* import */
pModule = PyImport_Import(PyString_FromString("great_module"));
/* great_module.great_function */
pFunc = PyObject_GetAttrString(pModule, "great_function");
/* build args */
pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs,0, PyInt_FromLong(a));
/* call */
pValue = https://www.04ip.com/post/PyObject_CallObject(pFunc, pArgs);
res = PyInt_AsLong(pValue);
return res;
}
从上述代码可以窥见Python内部运行的方式:
所有Python元素,module、function、tuple、string等等,实际上都是PyObject 。C语言里操纵它们,一律使用PyObject * 。
Python的类型与C语言类型可以相互转换 。Python类型XXX转换为C语言类型YYY要使用PyXXX_AsYYY函数;C类型YYY转换为Python类型XXX要使用PyXXX_FromYYY函数 。
也可以创建Python类型的变量 , 使用PyXXX_New可以创建类型为XXX的变量 。
若a是Tuple,则a[i] = b对应于 PyTuple_SetItem(a,i,b),有理由相信还有一个函数PyTuple_GetItem完成取得某一项的值 。
不仅Python语言很优雅,Python的库函数API也非常优雅 。
现在我们得到了一个C语言的函数了,可以写一个main测试它
#include Python.h
int great_function_from_python(int a);
int main(int argc, char *argv[]) {
Py_Initialize();
printf("%d",great_function_from_python(2));
Py_Finalize();
}
编译的方式就用本节开头使用的方法 。
在Linux/Mac OSX运行此示例之前,可能先需要设置环境变量:
bash:
export PYTHONPATH=.:$PYTHONPATH
csh:
setenv PYTHONPATH .:$PYTHONPATH
如何实现C/C与Python的通信用C/C对脚本语言的功能扩展是非常常见的事情,Python也不例外 。除了SWIG,市面上还有若干用于Python扩展的工具包 , 比较知名的还有Boost.Python、SIP等,此外,Cython由于可以直接集成C/C代码,并方便的生成Python模块,故也可以完成扩展Python的任务 。答主在这里选用SWIG的一个重要原因是,它不仅可以用于Python,也可以用于其他语言 。如今SWIG已经支持C/C的好基友Java,主流脚本语言Python、Perl、Ruby、PHP、JavaScript、tcl、Lua,还有Go、C#,以及R 。SWIG是基于配置的,也就是说,原则上一套配置改变不同的编译方法就能适用各种语言(当然,这是理想情况了……)SWIG的安装方便 , 有Windows的预编译包,解压即用,绿色健康 。主流Linux通常集成swig的包,也可以下载源代码自己编译,SWIG非常小巧 , 通常安装不会出什么问题 。用SWIG扩展Python,你需要有一个待扩展的C/C库 。这个库有可能是你自己写的,也有可能是某个项目提供的 。这里举一个不浮夸的例子:希望在Python中用到SSE4指令集的CRC32指令 。首先打开指令集的文档可以看到有6个函数 。分析6个函数的原型,其参数和返回值都是简单的整数 。于是书写SWIG的配置文件(为了简化起见 , 未包含2个64位函数):/*File:mymodule.i*/%modulemymodule%{#include"nmmintrin.h"%}int_mm_popcnt_u32(unsignedintv);unsignedint_mm_crc32_u8(unsignedintcrc,unsignedcharv);unsignedint_mm_crc32_u16(unsignedintcrc,unsignedshortv);unsignedint_mm_crc32_u32(unsignedintcrc,unsignedintv);接下来使用SWIG将这个配置文件编译为所谓PythonModuleWrapperswig-pythonmymodule.i得到一个mymodule_wrap.c和一个mymodule.py 。把它编译为Python扩展:Windows:cl/LDmymodule_wrap.c/o_mymodule.pyd-IC:\Python27\includeC:\Python27\libs\python27.libLinux:gcc-fPIC-sharedmymodule_wrap.c-o_mymodule.so-I/usr/include/python2.7/-lpython2.7注意输出文件名前面要加一个下划线 。现在可以立即在Python下使用这个module了:importmymodulemymodule._mm_popcnt_u32(10)2回顾这个配置文件分为3个部分:定义module名称mymodule,通常,module名称要和文件名保持一致 。%{%}包裹的部分是C语言的代码 , 这段代码会原封不动的复制到mymodule_wrap.c欲导出的函数签名列表 。直接从头文件里复制过来即可 。还记得本文第2节的那个great_function吗?有了SWIG,事情就会变得如此简单:/*great_module.i*/%modulegreat_module%{intgreat_function(inta){returna 1;}%}intgreat_function(inta);换句话说,SWIG自动完成了诸如Python类型转换、module初始化、导出代码表生成的诸多工作 。对于C,SWIG也可以应对 。例如以下代码有C类的定义://great_class.h#ifndefGREAT_CLASS#defineGREAT_CLASSclassGreat{private:ints;public:voidsetWall(int_s){s=_s;};intgetWall(){returns;};};#endif//GREAT_CLASS对应的SWIG配置文件/*great_class.i*/%modulegreat_class%{#include"great_class.h"%}%include"great_class.h"这里不再重新敲一遍class的定义了,直接使用SWIG的%include指令SWIG编译时要加-c这个选项,生成的扩展名为cxxswig-c-pythongreat_class.iWindows下编译:cl/LDgreat_class_wrap.cxx/o_great_class.pyd-IC:\Python27\includeC:\Python27\libs\python27.libLinux , 使用C的编译器g-fPIC-sharedgreat_class_wrap.cxx-o_great_class.so-I/usr/include/python2.7/-lpython2.7在Python交互模式下测试:importgreat_classc=great_class.Great()c.setWall(5)c.getWall()5也就是说C的class会直接映射到PythonclassSWIG非常强大,对于Python接口而言,简单类型 , 甚至指针,都无需人工干涉即可自动转换,而复杂类型 , 尤其是自定义类型,SWIG提供了typemap供转换 。而一旦使用了typemap,配置文件将不再在各个语言当中通用 。参考资料:SWIG的官方文档 , 质量比较高 。SWIGUsersManual有个对应的中文版官网,很多年没有更新了 。写在最后:由于CPython自身的结构设计合理,使得Python的C/C扩展非常容易 。如果打算快速完成任务,Cython(C/C调用Python)和SWIG(Python调用C/C)是很不错的选择 。但是,一旦涉及到比较复杂的转换任务 , 无论是继续使用Cython还是SWIG,仍然需要学习Python源代码 。本文使用的开发环境:Python2.7.10Cython0.22SWIG3.0.6Windows10x64RTMCentOS7.1AMD64MacOSX10.10.4文中所述原理与具体环境适用性强 。文章所述代码均用于演示 , 缺乏必备的异常检查
python调用c函数Python是解释性语言, 底层就是用c实现python与c函数交互的, 所以用python调用C是很容易python与c函数交互的, 下面就总结一下各种调用python与c函数交互的方法, 给出例子, 所有例子都在ubuntu9.10, python2.6下试过
1. Python 调用 C (base)
想在python中调用c函数, 如这儿的fact
#include Python.h
int fact(int n)
{
if (n = 1)
return 1;
else
return n * fact(n - 1);
}
PyObject* wrap_fact(PyObject* self, PyObject* args)
{
int n, result;
if (! PyArg_ParseTuple(args, "i:fact", n))
return NULL;
result = fact(n);
return Py_BuildValue("i", result);
}
static PyMethodDef exampleMethods[] =
{
{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
{NULL, NULL}
};
void initexample()
{
PyObject* m;
m = Py_InitModule("example", exampleMethods);
}
把这段代码存为wrapper.c, 编成so库,
gcc -fPIC wrapper.c -o example.so -shared-I/usr/include/python2.6 -I/usr/lib/python2.6/config
然后在有此so库的目录, 进入python, 可以如下使用
import example
example.fact(4)
2. Python 调用 C(base)
在python中调用C类成员函数, 如下调用TestFact类中的fact函数,
#include Python.h
class TestFact{
public:
TestFact(){};
~TestFact(){};
int fact(int n);
};
int TestFact::fact(int n)
{
if (n = 1)
return 1;
else
return n * (n - 1);
}
int fact(int n)
{
TestFact t;
return t.fact(n);
}
PyObject* wrap_fact(PyObject* self, PyObject* args)
{
int n, result;
if (! PyArg_ParseTuple(args, "i:fact", n))
return NULL;
result = fact(n);
return Py_BuildValue("i", result);
}
static PyMethodDef exampleMethods[] =
{
{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
{NULL, NULL}
};
extern "C"//不加会导致找不到initexample
void initexample()
{
PyObject* m;
m = Py_InitModule("example", exampleMethods);
}
把这段代码存为wrapper.cpp, 编成so库,
g-fPIC wrapper.cpp -o example.so -shared -I/usr/include/python2.6 -I/usr/lib/python2.6/config
然后在有此so库的目录, 进入python, 可以如下使用
import example
example.fact(4)
3. Python 调用 C(Boost.Python)
Boost库是非常强大的库, 其中的python库可以用来封装c被python调用, 功能比较强大, 不但可以封装函数还能封装类, 类成员.
首先在ubuntu下安装boost.python, apt-get install libboost-python-dev
#include boost/python.hpp
char const* greet()
{
return "hello, world";
}
BOOST_PYTHON_MODULE(hello)
{
using namespace boost::python;
def("greet", greet);
}
把代码存为hello.cpp, 编译成so库
ghello.cpp -o hello.so -shared -I/usr/include/python2.5 -I/usr/lib/python2.5/config -lboost_python-gcc42-mt-1_34_1
此处python路径设为python与c函数交互你的python路径, 并且必须加-lboost_python-gcc42-mt-1_34_1, 这个库名不一定是这个, 去/user/lib查
然后在有此so库的目录, 进入python, 可以如下使用
import hello
hello.greet()
'hello, world'
4. python 调用 c(ctypes)
ctypes is an advanced ffi (Foreign Function Interface) package for Python 2.3 and higher. In Python 2.5 it is already included.
ctypes allows to call functions in dlls/shared libraries and has extensive facilities to create, access and manipulate simple and complicated C data types in Python - in other words: wrap libraries in pure Python. It is even possible to implement C callback functions in pure Python.
#include Python.h
class TestFact{
public:
TestFact(){};
~TestFact(){};
int fact(int n);
};
int TestFact::fact(int n)
{
if (n = 1)
return 1;
else
return n * (n - 1);
}
extern "C"
int fact(int n)
{
TestFact t;
return t.fact(n);
}
将代码存为wrapper.cpp不用写python接口封装, 直接编译成so库,
g-fPIC wrapper.cpp -o example.so -shared -I/usr/include/python2.6 -I/usr/lib/python2.6/config
进入python, 可以如下使用
import ctypes
pdll = ctypes.CDLL('/home/ubuntu/tmp/example.so')
pdll.fact(4)
12
如何让python调用C和C代码要搞明白如何让python调用C/C代码(也就是写python的extension),你需要征服手册中的Extendingembedding厚厚的一章 。在昨天花了一个小时看地头晕脑胀 , 仍然不知道如何写python的extension后,查阅了一些其他书籍,最终在Python Programming On Win32书中找到了教程 。
1. 首先要明白的是 , 所谓的python扩展(也就是你提供给python的c/c代码,不一定是c/c代码,可以是其他语言写的代码)是一个dll,并且这个dll放在本机python安装目录下的DLLs目录下(譬如我机器上的路径是:F:/Program Files/Python25/DLLs),假如我们接下来要写的扩展module名为mb,python调用的代码为:import mbmb.showMsg("Python's really amazing, I kindda love it!")
2. 搭建环境 , 我们要使用python提供的c头文件和lib库来进行扩展的开发 。
在vs 2005下点击菜单 "工具"-"选项", 打开选项对话框,选择"项目和解决方案-VC目录", 然后在右边"显示以下内容的目录"得comboBox上选择"包含文件”,添加python的include目录(我的机器上是"F:/Program Files/Python25/include"),然后选择库文件,添加python的libs目录(我的机器上是"F:/Program Files/Python25/libs") 。
既然扩展是一个dll,接下来我们要建立一个“动态链接库”工程,然后开始写代码:
#include python.h //python.h是包含python一些定义的头文件,在python的include目录下/*我的python版本是2.5, 因为安装python后它没提供debug下的lib库文件 , 因此你必须生成release版的dll ,
想要生成dll版本的,你要到python官网上自己去下载python源代码,当然你可以继续生成release版本的dll,但dll中包含调试信息*/#pragma comment(lib, "python25.lib")//先不管static PyObject* mb_showMsg(PyObject* self, PyObject *args);/*如果你的扩展是mb,那么必须实现一个initmb函数,并且从dll中导出这个函数,但我们在python中调用import mb时 , python会去dll里去调用
extern "C" __declspec(dllexport) void initmb(){/*当调用mb.showMsg("Python's really amazing, I kindda love it!")时, 相当于你告诉python我有一个showMsg函数,我们怎么告诉python去调用我们dll里的mb_showMsg函数呢?技巧就是下面的方式,定义一个字典数据结构,key = showMsg, value =https://www.04ip.com/post/mb_showMsg,METH_VARARGS是函数调用方式,仔细查手册吧*/static PyMethodDef mbMethods[] = {
{"showMsg", mb_showMsg, METH_VARARGS},
{NULL, NULL, NULL} /*sentinel,哨兵,用来标识结束*/};//告诉python我们的模块名叫mb, 模块包含的函数都在mbMethods字典里
PyObject *m = Py_InitModule("mb", mbMethods);}/*接下来实现核心功能showMsg*///第一个self参数我们用不着 , 具体查手册,第二个参数是python传给我们的参数,它是一个python的参数tuple
static PyObject* mb_showMsg(PyObject* self, PyObject *args){//我们的showMsg函数需要的是一个字符串参数
const char* msg = NULL;/*调用特殊参数解码python传递给我们的参数,s是string,我们传递接收参数的变量地址,
如果你的功能函数需要两个参数,在PyArg_parseTuple后面继续添加接受参数的变量地址 ,
这个函数的原型是类似printf的不定参数的形式
PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...);*/if (!PyArg_ParseTuple(args, "s", msg))
return NULL;//调用MBint r = ::MessageBox(NULL, "hello", "Caption:Form C module", MB_ICONINFORMATION | MB_OK);//返回值return Py_BuildValue("i", r);}将上面这段混杂着大量注释的代码拷贝到你的编辑器里,然后编译生成mb.dll,修改后缀成mb.pyd,然后拷贝到python的DLLs目录下,打开idle(python的交互程序),写入代码:import mbmb.showMsg("Python's really amazing, I kindda love it!")
怎样让Python脚本与C程序互相调用二、Python调用C/C\x0d\x0a\x0d\x0a\x0d\x0a1、Python调用C动态链接库\x0d\x0a\x0d\x0aPython调用C库比较简单python与c函数交互 , 不经过任何封装打包成so,再使用python的ctypes调用即可 。\x0d\x0a(1)C语言文件python与c函数交互:pycall.c\x0d\x0a\x0d\x0a[html] view plain copy \x0d\x0a/***gcc -o libpycall.so -shared -fPIC pycall.c*/\x0d\x0a#include\x0d\x0a#include\x0d\x0aint foo(int a, int b)\x0d\x0a{\x0d\x0aprintf("you input %d and %d\n", a, b);\x0d\x0areturn a b;\x0d\x0a}\x0d\x0a(2)gcc编译生成动态库libpycall.so:gcc -o libpycall.so -shared -fPIC pycall.c 。使用g编译生成C动态库的代码中的函数或者方法时,需要使用extern "C"来进行编译 。\x0d\x0a(3)Python调用动态库的文件:pycall.py\x0d\x0a\x0d\x0a[html] view plain copy \x0d\x0aimport ctypes\x0d\x0all = ctypes.cdll.LoadLibrary\x0d\x0alib = ll("./libpycall.so")\x0d\x0alib.foo(1, 3)\x0d\x0aprint '***finish***'\x0d\x0a(4)运行结果:\x0d\x0a\x0d\x0a\x0d\x0a2、Python调用C(类)动态链接库 \x0d\x0a\x0d\x0a需要extern "C"来辅助,也就是说还是只能调用C函数,不能直接调用方法,但是能解析C方法 。不是用extern "C",构建后的动态链接库没有这些函数的符号表 。\x0d\x0a(1)C类文件:pycallclass.cpp\x0d\x0a\x0d\x0a[html] view plain copy \x0d\x0a#include\x0d\x0ausing namespace std;\x0d\x0a\x0d\x0aclass TestLib\x0d\x0a{\x0d\x0apublic:\x0d\x0avoid display();\x0d\x0avoid display(int a);\x0d\x0a};\x0d\x0avoid TestLib::display() {\x0d\x0acout\x0d\x0ausing namespace std;\x0d\x0aint test()\x0d\x0a{\x0d\x0aint a = 10, b = 5;\x0d\x0areturn a b;\x0d\x0a}\x0d\x0aint main()\x0d\x0a{\x0d\x0acout\x0d\x0a#include\x0d\x0a#include\x0d\x0a\x0d\x0aint fac(int n)\x0d\x0a{\x0d\x0aif (n
回答于 2022-11-16
python与c函数交互的介绍就聊到这里吧,感谢你花时间阅读本站内容 , 更多关于c和python交互、python与c函数交互的信息别忘了在本站进行查找喔 。
推荐阅读
- 如何练习游戏动作,游戏训练方法
- 小米4手机顶配什么,小米4高配
- pdf格式怎么填写,pdf的格式怎么弄
- centos自带mysql密码是多少的简单介绍
- 如何命令启动oracle 命令行启动oracle
- 农村电商如何提高销售业绩,农村电商如何提高销售业绩水平
- 背景图片css兼容,背景图片css兼容性
- 微信视频号怎么放慢,微信视频号怎么放慢速度
- WINDOWS系统人群的简单介绍