在Python中包装C库:C,Cython或ctypes?

发布于 2021-01-29 15:00:16

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

  1. 在C中创建一个实际的扩展模块。可能有点过头了,我还想避免学习扩展编写的开销。
  2. 使用Cython将C库的相关部分公开给Python。
  3. 使用Pythonctypes与外部库进行通信,从而完成整个工作。

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

两种选择都有其他优点/缺点吗?您推荐哪种方法?


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

由于没有一个单一的真实答案,因此接受一个答案多少有些武断。我选择了FogleBird的答案,因为它提供了对ctypes的一些很好的了解,并且它也是当前投票最高的答案。但是,我建议您阅读所有答案以获得一个很好的概述。

再次感谢。

关注者
0
被浏览
71
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

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

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

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

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

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

    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中。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看