在Python中包装C库:C,Cython或ctypes? [英] Wrapping a C library in Python: C, Cython or ctypes?

查看:91
本文介绍了在Python中包装C库:C,Cython或ctypes?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从Python应用程序调用C库。我不想包装整个API,只包装与我的情况相关的函数和数据类型。正如我所看到的,我有三个选择:

I want to call a C library from a Python application. I don't want to wrap the whole API, only the functions and datatypes that are relevant to my case. As I see it, I have three choices:


  1. 在C中创建一个实际的扩展模块。可能是矫kill过正,我也想避免学习扩展写作的开销。

  2. 使用 Cython 公开相关的部分,从C库到Python。

  3. 使用 ctypes 与外部库进行通讯。

  1. Create an actual extension module in C. Probably overkill, and I'd also like to avoid the overhead of learning extension writing.
  2. Use Cython to expose the relevant parts from the C library to Python.
  3. Do the whole thing in Python, using ctypes to communicate with the external library.

我不确定2)还是3)是更好的选择。 3)的优点是 ctypes 是标准库的一部分,生成的代码将是纯Python–。尽管我不确定该优势实际上有多大。

I'm not sure whether 2) or 3) is the better choice. The advantage of 3) is that ctypes is part of the standard library, and the resulting code would be pure Python – although I'm not sure how big that advantage actually is.

这两种选择是否都有更多优势/劣势?您推荐哪种方法?

Are there more advantages / disadvantages with either choice? Which approach do you recommend?

编辑:感谢您的所有回答,他们提供了对于任何想做类似事情的人来说都是很好的资源。当然,仍需针对单个案例做出决定-没有人会回答这是对的事情。就我自己而言,我可能会使用ctypes,但我也期待在其他项目中试用Cython。

Thanks for all your answers, they provide a good resource for anyone looking to do something similar. The decision, of course, is still to be made for the single case—there's no one "This is the right thing" sort of answer. For my own case, I'll probably go with ctypes, but I'm also looking forward to trying out Cython in some other project.

答案是,接受一个人有些武断。我选择了FogleBird的答案,因为它可以很好地了解ctypes,并且它也是目前投票最多的答案。但是,我建议您阅读所有答案以获得一个很好的概述。

With there being no single true answer, accepting one is somewhat arbitrary; I chose FogleBird's answer as it provides some good insight into ctypes and it currently also is the highest-voted answer. However, I suggest to read all the answers to get a good overview.

再次感谢。

推荐答案

ctypes 是快速完成它的最佳选择,并且在您仍在编写Python的情况下很高兴与之合作!

ctypes is your best bet for getting it done quickly, and it's a pleasure to work with as you're still writing Python!

我最近包装了一个 FTDI 驱动程序,用于与USB芯片进行通信使用ctypes,这很棒。我完成了所有工作,并在不到一个工作日的时间内完成了工作。 (我只实现了我们需要的功能,大约有15个功能。)

I recently wrapped an FTDI driver for communicating with a USB chip using ctypes and it was great. I had it all done and working in less than one work day. (I only implemented the functions we needed, about 15 functions).

我们以前使用的是第三方模块 PyUSB ,出于相同的目的。 PyUSB是实际的C / Python扩展模块。但是PyUSB在阻止读写时并没有释放GIL,这给我们带来了问题。因此,我使用ctypes编写了自己的模块,该模块会在调用本机函数时释放GIL。

We were previously using a third-party module, PyUSB, for the same purpose. PyUSB is an actual C/Python extension module. But PyUSB wasn't releasing the GIL when doing blocking reads/writes, which was causing problems for us. So I wrote our own module using ctypes, which does release the GIL when calling the native functions.

要注意的一件事是ctypes不会知道 #define 常量和正在使用的库中的内容,只有函数,因此您必须在自己的代码中重新定义这些常量。

One thing to note is that ctypes won't know about #define constants and stuff in the library you're using, only the functions, so you'll have to redefine those constants in your own code.

下面是一个示例,显示了代码最终的外观(大量内容被删除,只是试图向您展示其要旨):

Here's an example of how the code ended up looking (lots snipped out, just trying to show you the gist of it):

from ctypes import *

d2xx = WinDLL('ftd2xx')

OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3

...

def openEx(serial):
    serial = create_string_buffer(serial)
    handle = c_int()
    if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
        return Handle(handle.value)
    raise D2XXException

class Handle(object):
    def __init__(self, handle):
        self.handle = handle
    ...
    def read(self, bytes):
        buffer = create_string_buffer(bytes)
        count = c_int()
        if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
            return buffer.raw[:count.value]
        raise D2XXException
    def write(self, data):
        buffer = create_string_buffer(data)
        count = c_int()
        bytes = len(data)
        if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
            return count.value
        raise D2XXException

有人做了有关各种选项的一些基准

如果不得不包装带有许多类/模板/等的C ++库,我可能会更加犹豫。但是ctypes可以很好地与结构配合使用,甚至可以回调进入Python。

I might be more hesitant if I had to wrap a C++ library with lots of classes/templates/etc. But ctypes works well with structs and can even callback into Python.

这篇关于在Python中包装C库:C,Cython或ctypes?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆