用 Python ctypes 来调用 C/C++ 编写的第三方库
文章目录
如果需要用 Python 调用 C/C++ 编写的第三方库(这些第三方库很可能就是你之前写出来的),只是需要一个脚本语言来粘合它们。这个时候,用 Python ctypes 可以很方便地实现调用。
StackOverflow 上的 Calling C/C++ from python 这个主题介绍了 ctypes 最简单的入门方法,概括如下:
- 如果是 C 函数库,则直接 load 这个库,然后调用即可;
- 如果是 C++ 函数库,则需要用 extern 关键字封装一个供 C 使用的函数,即把类隐藏到一些 C 风格的函数里,然后用 extern 标明这些函数,以方便外部调用。
这两种方法里,弄懂了 ctypes 调用 C++ 库的方法,就会用 ctypes 调用 C 函数库,对 C++ 库的基本方法如下。
例如,有一个 C++ 类 Foo:
#include <iostream> class Foo{ public: void bar(){ std::cout << "Hello" << std::endl; } };
再封装出下面 C 风格的接口函数:
extern "C" { Foo* Foo_new(){ return new Foo(); } void Foo_bar(Foo* foo){ foo->bar(); } }
把上面的代码编译成动态链接库:
g++ -c -fPIC foo.cpp -o foo.o g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
,然后再用 Python 写的代码来调用这个类,你可以把上面两个 C 接口函数写成 Python 类,或是直接调用:
from ctypes import cdll lib = cdll.LoadLibrary('./libfoo.so') class Foo(object): def __init__(self): self.obj = lib.Foo_new() def bar(self): lib.Foo_bar(self.obj)
,然后就可以在 Python 脚本里调用这个 Python 类了:
f = Foo() f.bar() #and you will see "Hello" on the screen
在 Windows 下用 Python ctypes 的方法和上面一样,只是有下面两点需要注意一下:
- 在编写 Python 代码时,刚开始链接所需的动态链接库时,最好使用绝对路径来 load,以减少出错概率,加快调试速度 在我按上面的方法编写好了上述代码时,一运行脚本,则提示如下错误信息:
<pre class="example">$ python Linkcpp.py
Traceback (most recent call last): File “Linkcpp.py”, line 2, in <module> lib = cdll.LoadLibrary(’./LinkExample’) File “C:\Python27\lib\ctypes_init_.py”, line 431, in LoadLibrary return self.dlltype(name) File “C:\Python27\lib\ctypes_init.py”, line 353, in init self._handle = _dlopen(self._name, mode) WindowsError: [Error 126]
这是因为我在代码里是使用了这样的代码来导入动态链接库:
<pre class="src src-python"><span style="color: #8ac6f2;font-weight: bold">from</span> ctypes <span style="color: #8ac6f2;font-weight: bold">import</span> cdll
lib = cdll.LoadLibrary(’./LinkExample’)
如果把 ./LinkExample 这句换成 Windows 下的绝对路径 E:/PythonCode/LinkCpp/LinkExample,则没有错误提示了。当然,你直接把 ./LinkExample 换成 LinkExample 也可以找到该链接库。
所以,刚开始的时候,使用绝对路径,以确保你不会纠结于能不能找到链接库之类的问题。
在运行上述脚本的时候,出现 WindowsError: [Error 126] 的错误,无非就是[两个原因](http://stackoverflow.com/questions/10411709/windowserror-error-126-when-loading-a-dll-with-ctypes):
* 你的 DLL 没有正确地被加载;
* 你的 DLL 依赖的其它 DLL 没有被找到或是加载失败。
另外,注意一下,Windows 下因为库分为 lib 和 dll 两个文件,所以可以只输入库的名称即可,比如说你要链接 LinkExample.dll 库,则可以在 ctypes 里只需要声明链接名为 LinkExample 库即可。
- 如果是 C++ 写的库,需要用上 extern 关键字,这个和一般的供 C 调用的 C++ 库头文件是一样的 在 extern 声明的函数里,可以使用 C++ 里 C 没有的关键字,比如我的函数就是这样声明的:
<pre class="src src-c++"><span style="color: #8ac6f2;font-weight: bold">extern</span> <span style="color: #92a65e;font-weight: bold">int</span> <span style="color: #cae682">linkExample</span>(<span style="color: #8ac6f2;font-weight: bold">const</span> <span style="color: #92a65e;font-weight: bold">int</span> <span style="color: #cae682">index</span>, <span style="color: #8ac6f2;font-weight: bold">const</span> <span style="color: #92a65e;font-weight: bold">char</span>* <span style="color: #cae682">name</span>);
上面代码可以从 Python 调用运行。
extern 这个关键字在 C++ 里的用法比较复杂,在这里就是要声明一个供 C 使用的外部函数,而这个函数本身,却可以使用 C++ 的语法,包括 const 关键字等。
文章作者 cookwhy
上次更新 2013-03-13