我想从Python应用程序调用C库。我不想包装整个API,只包装与我的案例相关的函数和数据类型。在我看来,我有三个选择:

用c语言创建一个实际的扩展模块,这可能有点过分,而且我还想避免学习编写扩展的开销。 使用Cython将相关部分从C库公开到Python。 在Python中完成所有的事情,使用ctypes与外部库通信。

我不知道2)还是3)哪个更好。3)的优点是ctypes是标准库的一部分,生成的代码将是纯Python——尽管我不确定这个优点到底有多大。

这两种选择有更多的优点/缺点吗?你推荐哪种方法?


编辑:感谢你的回答,它们为任何想做类似事情的人提供了很好的资源。当然,这个决定仍然是针对单一情况做出的——没有一个“这是正确的事情”之类的答案。对于我自己的情况,我可能会使用ctypes,但我也期待在其他一些项目中尝试Cython。

由于没有唯一的正确答案,接受一个答案就有些武断了;我选择了FogleBird的答案,因为它提供了一些关于ctypes的很好的见解,而且它也是目前投票最多的答案。然而,我建议阅读所有的答案,以获得一个良好的概述。

再次感谢。


当前回答

就我个人而言,我会用C写一个扩展模块。不要被Python C扩展吓倒——它们写起来一点都不难。文档是非常清晰和有用的。当我第一次用Python写一个C扩展时,我想我花了大约一个小时来弄清楚如何写一个——根本没有多少时间。

其他回答

我知道这是一个老问题,但是当您在谷歌上搜索ctypes vs cython之类的东西时,会出现这个问题,这里的大多数答案都是由那些已经精通cython或c的人编写的,这可能无法反映您需要投入学习这些来实现您的解决方案的实际时间。这两方面我都是初学者。我以前从未接触过cython,对c/c++也没有什么经验。

在过去的两天里,我一直在寻找一种方法,将我代码中性能较重的部分委托给比python更低级的东西。我用ctypes和Cython实现了我的代码,它主要由两个简单的函数组成。

我有一个巨大的字符串列表需要处理。注意列表和字符串。 这两种类型都不完全对应于c中的类型,因为python字符串默认是unicode,而c字符串不是。python中的列表只是c的NOT数组。

以下是我的看法。使用cython。它更流畅地集成到python中,而且一般来说更容易使用。当出现错误时,ctypes只会抛出段错误,至少cython会在可能的情况下提供带有堆栈跟踪的编译警告,并且可以使用cython轻松返回有效的python对象。

下面是关于我需要投入多少时间来实现相同的功能的详细说明。顺便说一下,我做了很少的C/ c++编程:

Ctypes: About 2h on researching how to transform my list of unicode strings to a c compatible type. About an hour on how to return a string properly from a c function. Here I actually provided my own solution to SO once I have written the functions. About half an hour to write the code in c, compile it to a dynamic library. 10 minutes to write a test code in python to check if c code works. About an hour of doing some tests and rearranging the c code. Then I plugged the c code into actual code base, and saw that ctypes does not play well with multiprocessing module as its handler is not pickable by default. About 20 minutes I rearranged my code to not use multiprocessing module, and retried. Then second function in my c code generated segfaults in my code base although it passed my testing code. Well, this is probably my fault for not checking well with edge cases, I was looking for a quick solution. For about 40 minutes I tried to determine possible causes of these segfaults. I split my functions into two libraries and tried again. Still had segfaults for my second function. I decided to let go of the second function and use only the first function of c code and at the second or third iteration of the python loop that uses it, I had a UnicodeError about not decoding a byte at the some position though I encoded and decoded everthing explicitely.

在这一点上,我决定寻找一个替代品,并决定研究cython:

Cython 10分钟阅读cython hello world。 用15分钟检查SO如何使用setuptools而不是distutils使用cython。 10分钟关于cython类型和python类型的阅读。我了解到我可以使用大多数内置的python类型进行静态类型。 15分钟用cython类型重新注释我的python代码。 10分钟的修改我的setup.py使用编译模块在我的代码库。 将模块直接插入到多处理版本的代码库中。它的工作原理。

郑重声明,我当然没有衡量我投资的准确时机。这很可能是由于在处理ctypes时需要花费太多精力,所以我对时间的感知有点太专注了。但是它应该传达处理cython和ctypes的感觉

就我个人而言,我会用C写一个扩展模块。不要被Python C扩展吓倒——它们写起来一点都不难。文档是非常清晰和有用的。当我第一次用Python写一个C扩展时,我想我花了大约一个小时来弄清楚如何写一个——根本没有多少时间。

当你已经有一个编译好的库blob要处理(比如OS库)时,ctypes非常有用。然而,调用开销很严重,所以如果您将对库进行大量调用,并且无论如何都要编写C代码(或者至少编译它),那么我建议您使用cython。这并不需要做更多的工作,而且使用生成的pyd文件会更快、更python化。

我个人倾向于使用cython来快速加速python代码(循环和整数比较是cython特别擅长的两个领域),当涉及到其他库的一些更复杂的代码/包装时,我将转向Boost.Python。提振。Python的设置可能很挑剔,但一旦你让它工作了,它就可以简单地包装C/ c++代码。

cython在包装numpy方面也很出色(这是我从SciPy 2009会议中了解到的),但我没有使用过numpy,因此不能对此进行评论。

我再举一个例子:SWIG

它很容易学,做了很多正确的事情,并支持更多的语言,所以花时间学习它是非常有用的。

如果您使用SWIG,您将创建一个新的python扩展模块,但是SWIG将为您完成大部分繁重的工作。

如果您的目标是Windows并选择包装一些专有的c++库,那么您可能很快就会发现msvcrt***.dll (Visual c++ Runtime)的不同版本略有不兼容。

这意味着您可能无法使用Cython,因为产生了包装器。pyd链接到msvcr90.dll (Python 2.7)或msvcr100.dll (Python 3.x)。如果您正在包装的库链接到不同版本的运行时,那么您就不走运了。

然后,为了使事情正常工作,您需要为c++库创建C包装器,将包装器dll链接到与您的c++库相同版本的msvcrt***.dll。然后使用ctypes在运行时动态加载你的手卷包装器dll。

所以有很多小细节,在下面的文章中有详细的描述:

美丽的本地库(Python): http://lucumr.pocoo.org/2013/8/18/beautiful-native-libraries/