python调用c/c++概述
因为实验室项目的进度,需要把之前用c写的接口程序转为python。因为c的执行速度更高,所以考虑保持原来的c代码不变,用python直接调用其中的函数。实际中也经常用这样的方法来给python加速,参考博客[1]中就提到了,因为他们的程序中有特别大的双层循环,还有位操作,所以效果明显——加速后C运行速度比python快了1000倍。这就来到了如何在python中调用c/c++代码的问题。
在python和c/c++交互使用中,有以下几个不同方面:
本文的重点在介绍python调用c/c++动态链接库,特别是应用ctypes进行类型转换,包括自己在尝试过程中遇到的问题。其他部分简单介绍并给一些博客链接。
一、 python调用c/c++动态链接库
python调用动态链接库这件事本身浅显易懂。总体就三步:
1、 编写c语言文件
2、 gcc编译生成动态链接库
3、 python导入动态链接库并调用函数
导入ctypes,通过ctypes导入动态链接库。
这样就成功将c嵌入到python中了。
但是问题就来了。我们一般调用的函数不会这么简单的。通常我们都是希望在c中完成复杂的计算,再把结果返回给python。所以我们需要通过调用来传入参数(很可能是数组),同时返回结果(往往也是数组)。这就需要提一下ctypes到底是个什么东西了。
ctypes
ctypes is a foreign function library forPython. It provides C compatible data types, and allows calling functions inDLLs or shared libraries. It can be used to wrap these libraries in purePython.[3]
[3]中是官方的说明手册,解释的都很清楚,强烈推荐!
ctypes可以处理传参的参数类型转换:
中间那列是c中的参数类型,右边是python参数类型。除了int, long, string,unicode string之外都需要转换,两者之间的转换可以通过ctypes类型完成。[4]中详细解释了传指针的方法,可以参考。
ctypes可以调用c/c++文件中的函数,这里需要指明传入参数的类型和返回参数的类型:
这个函数是我昨天写的,用于调用读取机械臂状态信息的c文件。第30行设定了传入参数类型(float指针),第31行设定了返回参数的类型(int)。c函数的计算结果保存在传入参数的所在地址,所以提前给ret_value开辟空间。之后就是调用函数和返回处理了。通过这样的方法,可以成功调用c代码,通过TCP通信获得机械臂的实时位置。
除了之前那张表中所列的参数类型,还可以自己定义结构体。可以参考[4]。不知为何,笔者自己想通过返回值传递float数组指针的测试失败了,返回结构体的指针是可行的,如下图。(感觉似乎犯了什么非常蠢的错误)
至此,这个问题还没有完。剩下的问题都是编译的问题:
1、 OSError: undefined Symbol
出现了这个错误,是因为gcc/g++编译的时候没有包含依赖库和头文件。
可以用ldd -r xxx.so来查看是否是动态链接库本身的问题。
2、 c与cpp
对于.c文件,使用的是c编译器,编译指令可以为:
gcc -o lib.so -fPIC -shared xxx.c
但是如果使用c++编译器,例如g++,或者仅仅是把源代码命名为xxx.cpp,都会导致编译后的动态库无法调用。出现AttributeError:undefined symbol的错误,如下图。
此时的解决办法是,把源代码.cpp文件中所有函数前面都加上extern “C”,强迫使用c编译器进行编译。[5]
编译部分的东西不是很懂,我理解的大概是这样。希望研究生期间有机会能上编译、优化的课程。
二、 python调用c/c++可执行程序
将c/c++代码编译为可执行程序,之后在python中执行系统程序,可以通过argv传入参数,并读取程序运行的输出。
但这种方法有诸多不便之处。例如一个可执行程序只能执行有限的功能,所有参数都必须传入main函数,并通过print输出结果。如果想让c/c++代码作为一个多功呢个库,这就很难实现。
三、 扩展python
需要#include<Python.h>。
原理就是将c/c++代码当作python的模块,与用python代码编写的功能模块一样,在python中对这些模块进行调用而无需了解模块中的实现细节。
为了让python解释器能够理解模块中执行的内容,需要中间过渡,接口的代码称为样板代码。为Python创建扩展需要三个主要的步骤:创建应用程序代码、利用样板来包装代码和编译与测试。即先编写实现相应功能的c代码,用python接口来包装所有函数,最后在python中调用测试。具体见[2]。
四、 c/c++调用python
需要#include<Python.h>。
把python文件当作文本形式的动态链接库,只要不改变接口,需要的时候还可以修改python库。但是c文件编译好之后就不好改了。这里注意,编译的时候要手动指定python的include路径和链接路径。[2]
参考博客:
[1] 利用ctypes给python加速. https://blog.****.net/thesby/article/details/76283807.
[2]Python实例浅谈之三Python与c/c++相互调用. https://www.cnblogs.com/apexchu/p/5015961.html.
[3] PythonDocumentation: ctypes. https://docs.python.org/2/library/ctypes.html.
[4]python ctypes 探究 ---- python与c交互. http://www.cnblogs.com/night-ride-depart/p/4907613.html.
[5] linux平台上面python调用c. http://www.cnblogs.com/laodageblog/p/3757867.html.