使用ctypes来扩展Python

为了扩展Python,咱们能够用C/C++编写模块,可是这要求对Python的底层有足够的了解,包括Python对象模型、经常使用模块、引用计数等,门槛较高,且不方便利用现有的C库。而 ctypes 则另辟蹊径,经过封装dlopen/dlsym之类的函数,并提供对C中数据结构的包装/解包,让Python可以加载动态库、导出其中的函数直接加以利用。html

快速上手

最简单的,咱们能够从 libc 开始:python

felix021@ubserver:~/code$ python
Python 2.7.3 (default, Jul  5 2013, 08:39:51) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> libc = ctypes.CDLL("libc.so.6")
>>> libc.printf("hello, %s! %d + %d = %d\n", "world", 1, 2, 3)
hello, world! 1 + 2 = 3
25

因为ctypes库可以直接处理 None, integers, longs, byte strings 和 unicode strings 类型的转换,所以在这里不须要任何额外的操做就能完成任务。(注意,最后一行的25是printf的返回值,标识输出了25个字符)linux

本身动手

这里有一个最简单的例子——实现一个 int sum(int a, int b)windows

//file foo.c
#ifdef __WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif

DLLEXPORT int sum(int a, int b)
{
    return a + b;
}

编译之获得 foo.so:数组

$ gcc -fPIC -shared -o foo.so foo.c

而后在Python里头:数据结构

>>> foo = ctypes.CDLL("./foo.so")
>>> foo.sum(5, 3)
8

真是超级简单。函数

丰衣足食

下面来个稍微复杂点的例子:反转一个字符串。尽管ctypes能够直接处理string的转换,可是你不能直接修改string里的内容,因此这里须要变通一下。可能的解决方案有两个:code

  1. 在foo.c里面malloc一点空间而后返回。这样能够不修改原string,可是却要考虑释放该空间的问题,不太好处理。
  2. 让Python分配好空间,将原字符串拷贝进去,再让foo里面的函数来修改它。ctypes为这种方案提供了create_string_buffer这个函数,正适合。

那就让咱们用方案2来实现它:orm

//file foo.c
DLLEXPORT int reverse(char *str)
{
    int i, j, t;
    for (i = 0, j = strlen(str) - 1; i < j; i++, j--)
        t = str[i], str[i] = str[j], str[j] = t;
    return 0;
}

而后在Python里头:server

>>> s = ctypes.create_string_buffer("hello world!")
>>> foo.reverse(s)
0
>>> print s.value
!dlrow olleh

这样就OK啦。

补充说明

以上的操做都是在Linux下完成的,在Windows下基本上一致,除了windows不能直接load libc.so.6,而是应该找 msvcrt :

>>> libc = ctypes.cdll.msvcrt

或者直接导入文件

>>> libc = ctypes.CDLL("msvcr90.dll")

小结

前面演示了ctypes库的简单使用,已经可以完成许多任务了;可是它能够完成更多的任务,包括操做更复杂的例如结构体、数组等C数据结构,具体的这里不细说了,能够参考详细的官方文档

好了,就到这里。

相关文章
相关标签/搜索