Aug 16

使用ctypes来扩展Python 不指定

felix021 @ 2013-8-16 08:58 [IT » Python] 评论(1) , 引用(0) , 阅读(15499) | Via 本站原创 | |
为了扩展Python,我们可以[用C/C++编写模块](http://docs.python.org/2/extending/ ),但是这要求对Python的底层有足够的了解,包括Python对象模型、常用模块、引用计数等,门槛较高,且不方便利用现有的C库。而 [ctypes](http://docs.python.org/2/library/ctypes.html ) 则另辟蹊径,通过封装`dlopen/dlsym`之类的函数,并提供对C中数据结构的包装/解包,让Python能够加载动态库、导出其中的函数直接加以利用。

快速上手
-------

最简单的,我们可以从 libc 开始:
    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个字符)

自己动手
-------

[这里](http://wolfprojects.altervista.org/articles/dll-in-c-for-python/)有一个最简单的例子——实现一个 `int sum(int a, int b)`:

    //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里的内容,所以这里需要变通一下。可能的解决方案有两个:

1. 在foo.c里面malloc一点空间然后返回。这样可以不修改原string,但是却要考虑释放该空间的问题,不太好处理。

2. 让Python分配好空间,将原字符串拷贝进去,再让foo里面的函数来修改它。ctypes为这种方案提供了`create_string_buffer`这个函数,正适合。

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

    //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里头:

    >>> 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数据结构,具体的这里不细说了,可以参考详细的[官方文档](http://docs.python.org/2/library/ctypes.html)

好了,就到这里。

转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: http://www.felix021.com/blog/feed.php
ctqmumu
2013-11-20 13:25
我在想额外的两个问题:1. 能不能传入python定义结构体(map、list)。 2. 库函数的内存使用情况(内存位置,大小情况,能否使用共享内存等等...)uplook  不过看来应该是可以的,以后有空调研下
分页: 1/1 第一页 1 最后页
发表评论
表情
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
打开HTML
打开UBB
打开表情
隐藏
记住我
昵称   密码   *非必须
网址   电邮   [注册]