我一直在考虑写一个峰值拟合库一段时间。我非常了解Python并且计划在Python中实现所有内容,但是设想我最终可能必须以编译语言重新实现一些核心例程。
IIRC,Python的原始内容之一是原型语言,但是Python在允许函数,仿函数,对象传递给函数和方法方面相当自由,而我怀疑C或Fortran也不是这样。我应该知道如何设计我想要与编译语言接口的函数/类?这些潜在问题中有多少是由cTypes,bgen,SWIG,Boost.Python,Cython或Python SIP这些库处理的?
对于这个特殊的用例,(拟合库)我想让用户将数学函数(Guassian,Lorentzian等)定义为Python函数,然后可以通过编译的代码拟合库传递解释。传递和返回数组也很重要。
答案 0 :(得分:35)
最后一个问题,我真的可以给一个价值答案:)。
我为我的工作调查了f2py,boost.python,swig,cython和pyrex(光学测量技术博士)。我广泛使用swig,boost.python一些和pyrex和cython很多。我也用过ctypes。这是我的细分:
免责声明:这是我个人的经历。我没有参与任何这些项目。
<强>痛饮:强> 用c ++不能很好地发挥作用。它应该,但在链接步骤中命名错误问题是我在Linux和Linux上的一个主要问题。 Mac OS X.如果您有C代码并希望它与python接口,那么这是一个很好的解决方案。我根据自己的需要包装了GTS,并且需要编写一个我可以连接的C共享库。我不推荐它。
<强> ctypes的:强> 我用ctypes写了一个libdc1394(IEEE Camera库)包装器,这是一个非常简单的经验。您可以在https://launchpad.net/pydc1394上找到代码。将头文件转换为python代码需要做很多工作,但一切都可靠。如果要连接外部库,这是一种很好的方法。 Ctypes也在python的stdlib中,所以每个人都可以立即使用你的代码。这也是快速使用python中的新lib的好方法。我可以推荐它与外部库的接口。
Boost.Python :非常愉快。如果您已经拥有自己想要在python中使用的C ++代码,那就去做吧。以这种方式将c ++类结构转换为python类结构非常容易。如果你有python中需要的c ++代码,我推荐它。
Pyrex / Cython:使用Cython,而不是Pyrex。期。 Cython更先进,更有趣。如今,我用cython做了一切,我曾经用SWIG或Ctypes做过。如果你的python代码运行得太慢,这也是最好的方法。这个过程绝对是太棒了:你将你的python模块转换成cython模块,构建它们并保持分析和优化,就像它仍然是python(不需要更改工具)。然后,您可以应用与python代码混合的尽可能多(或尽可能少)的C代码。这比使用C重写应用程序的整个部分要快得多;你只重写了内循环。
计时:ctypes的呼叫开销最高(约700ns),其次是boost.python(322ns),然后是swig(290ns)。 Cython具有最低的呼叫开销(124ns)以及花费时间的最佳反馈(cProfile支持!)。这些数字来自我的盒子,调用一个简单的函数,从交互式shell返回一个整数;因此,模块导入开销不是定时的,只有函数调用开销。因此,通过分析和使用cython来快速获取python代码是最容易和最有效的。
摘要:对于您的问题,请使用Cython;)。我希望这个破坏对某些人有用。我很乐意回答任何剩下的问题。
编辑:我忘记提及:出于数字目的(即连接到NumPy)使用Cython;他们支持它(因为他们基本上为此目的开发了cython)。所以这应该是你决定的另一个+1。
答案 1 :(得分:10)
我没有使用SWIG或SIP,但我发现用boost.python编写Python包装器非常强大且相对容易使用。
我不清楚你在C / C ++和python之间传递类型的要求是什么,但是你可以通过将C ++类型暴露给python,或者通过使用泛型boost::python::object参数来轻松地做到这一点。你的C ++ API。您还可以注册转换器以自动将python类型转换为C ++类型,反之亦然。
如果您计划使用boost.python,tutorial是一个很好的起点。
我已经实现了一些类似于你需要的东西。我有一个C ++函数 接受python函数和图像作为参数,并将python函数应用于图像中的每个像素。
Image* unary(boost::python::object op, Image& im)
{
Image* out = new Image(im.width(), im.height(), im.channels());
for(unsigned int i=0; i<im.size(); i++)
{
(*out)[i] == extract<float>(op(im[i]));
}
return out;
}
在这种情况下,Image是一个暴露给python的C ++对象(一个带浮点像素的图像),op是一个python定义的函数(或者实际上是一个带有__call__属性的python对象)。然后,您可以按如下方式使用此函数(假设一元位于被调用的图像中,该图像还包含图像和加载函数):
import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)
至于使用带有boost的数组,我个人还没有这样做,但我知道使用boost可以将数组暴露给python的功能 - this可能会有所帮助。
答案 2 :(得分:6)
规划最终转换为已编译代码的最佳方法是将性能敏感部分编写为functional style中的简单函数模块(无状态且无副作用),接受并返回基本数据类型
这将提供从Python原型代码到最终编译代码的一对一映射,并允许您轻松使用ctypes并避免一大堆头痛。
对于峰值拟合,你几乎肯定需要使用数组,这会使事情变得复杂,但仍然非常适合ctypes。
如果你真的想要使用更复杂的数据结构,或修改传递的参数,SWIG或Python's standard C-extension interface会让你做你想做的事情,但会有一些麻烦。
对于您正在做的事情,您可能还需要查看NumPy,这可能会执行您希望推送到C的部分工作,以及提供some additional help in moving data back and forth between Python and C。
答案 3 :(得分:4)
f2py(numpy
的一部分)是SWIG和boost.python的一个更简单的替代方法,用于包装C / Fortran数字运算代码。
答案 4 :(得分:1)
根据我的经验,有两种简单的方法可以从Python代码调用C代码。还有其他方法,所有方法都更烦人和/或更详细。
第一个也是最简单的方法是将一堆C代码编译为一个单独的共享库,然后使用ctypes调用该库中的函数。不幸的是,传递除基本数据类型之外的任何东西都是非常重要的。
第二种最简单的方法是在C中编写Python模块,然后调用该模块中的函数。您可以将任何想要的内容传递给这些C函数,而无需跳过任何环节。从这些C函数中调用Python函数或方法很容易,如下所述:https://docs.python.org/extending/extending.html#calling-python-functions-from-c
我没有足够的SWIG经验来提供智能评论。虽然可以做一些事情,比如通过ctypes将自定义Python对象传递给C函数,或者用C语言定义新的Python类,但这些事情很烦人且冗长,我建议采用上述两种方法之一。
答案 5 :(得分:0)
Python允许将函数,函子,对象传递给函数和方法,而我怀疑C或Fortran的情况也是如此。
在C中,你不能将一个函数作为参数传递给函数,但是你可以传递一个函数指针,它就像一个函数一样好。
我不知道当您尝试集成C和Python代码时会有多大帮助,但我只是想澄清一个误解。
答案 6 :(得分:0)