使用Python代码进行原型设计,在编译之前。

22

我一直在考虑编写一个峰形拟合库。我对Python相当熟悉,并计划首先使用Python实现所有内容,但预计最终可能需要重新用编译语言实现一些核心例程。

据我所知,Python最初的任务之一是作为原型语言,然而Python在允许将函数、函数对象、对象传递给函数和方法方面非常自由,而我怀疑C或Fortran等语言不具备同样的特征。

我应该了解哪些设计函数/类的知识,以便将其接口到编译语言中?这些潜在问题的多少已经被cTypes、bgen、SWIGBoost.PythonCythonPython SIP等库处理?

对于这种特定用例(拟合库),我想象中允许用户定义数学函数(Guassian、Lorentzian等)作为Python函数,然后通过编译代码拟合库进行传递和解释。传递和返回数组也是必要的。

7个回答

36

终于有一个问题可以让我真正地回答了 :).

我对f2py、boost.python、swig、cython和pyrex进行了调查,这些都是我在光学测量技术博士工作中使用的工具。我广泛使用了swig,一些使用了boost.python和pyrex和cython。我也使用了ctypes。以下是我的总结:

声明: 这是我的个人经验。我没有参与其中任何项目。

swig: 不适用于c++。理论上应该支持,但在链接步骤中出现了命名混乱问题,这对我在linux和Mac OS X上带来了很大的麻烦。如果您有C代码并想要将其接口化到python中,它是一个好的解决方案。我包装了GTS以满足我的需求,并需要编写一个基本的C共享库来连接。我不建议使用它。

Ctypes: 我使用ctypes编写了一个libdc1394(IEEE摄像机库)包装器,这是一个非常简单的体验。你可以在https://launchpad.net/pydc1394上找到代码。将头文件转换为python代码需要很多工作,但接下来一切都可靠地工作。如果你想要接口化外部库,这是一个很好的方法。Ctypes也在python的stdlib中,因此每个人都可以立即使用您的代码。这也是快速在python中尝试新的lib的好方法。我可以推荐它来接口化外部libs。

Boost.Python:非常愉快。如果您已经拥有自己的C++代码并希望在python中使用它,请选择它。这种方式非常容易将c++类结构转换为python类结构。如果你需要在python中使用c++代码,我推荐你使用它。

使用Cython而不是Pyrex。这是绝对正确的选择。Cython更先进,更易于使用。现在,我所做的一切都用Cython来完成,这与以前使用SWIG或Ctypes没有任何区别。如果你有些运行缓慢的Python代码,那么Cython就是最佳解决方案。整个过程绝对fantastic:你将你的Python模块转化成Cython模块,构建它们,并像使用Python一样进行性能分析和优化(无需改变工具)。然后,你可以添加尽可能多(或尽可能少)的C代码到你的Python代码中。这比必须重新编写应用程序的整个部分要快得多;你只需要重写内循环即可。
调用开销最高的是ctypes(约为700ns),其次是boost.python(约为322ns),然后直接是swig(约为290ns)。Cython的调用开销最低(约为124ns),并且花费时间最少。这些数字来自我的计算机,在交互式shell中调用一个返回整数的微不足道的函数;因此,导入模块的开销未计时,只计算了函数调用的开销。因此,通过分析和使用Cython来使Python代码变快最容易和有效。
总之,针对你的问题,请使用Cython ; )。希望这篇文章对某些人有用。我很乐意回答任何剩下的问题。
编辑:我忘记提到:对于数值目的(即与NumPy的连接),请使用Cython;他们为此提供支持(因为他们基本上是为这个目的开发Cython)。因此,这应该是你的决定另一个加分项。

非常有价值的概述,谢谢。您是否有暴露高度模板化的C++的经验?我知道boost的方法是模板元编程,但他们是否实际支持暴露模板元编程库?此外,最近18个月中列表是否有任何更改? - daspostloch
没有,没有任何更改。一切仍然保持不变,但是我在过去一年中没有使用ctypes或swig进行工作。Cython现在相当合理地支持模板,所以我可能也会选择这里。然而,我没有非常模板化的代码(只有一些容器类),因此在这方面没有真正的经验。 - SirVer
我也使用ctypes封装了一些大型的C库,并且我向未来的开发者们推荐http://code.google.com/p/ctypesgen/,它真的帮了我很大的忙。 - Brian Larsen

10

我没有使用SWIG或SIP,但我发现使用boost.python编写Python包装器非常强大且相对易用。

我不清楚您在C/C++和python之间传递类型的要求是什么,但您可以通过将C++类型暴露给python或使用通用的boost::python::object参数到您的C++ API轻松实现。您还可以注册转换器以自动将python类型转换为C++类型,反之亦然。

如果您打算使用boost.python,则教程是一个很好的起点。

我已经实现了与您需要的类似的东西。我有一个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对象)。然后,您可以按以下方式使用此函数(假设unary位于调用图像中,该图像还包含Image和一个load函数):
import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)

关于使用boost的数组,我个人没有实践过,但我知道使用boost将数组暴露给Python的功能是可用的-这里可能会有所帮助。


6
为了计划最终转换为编译代码的过程,最好将性能敏感部分编写为简单函数模块,以 函数式风格(无状态且无副作用),接受和返回基本数据类型。
这将提供从您的 Python 原型代码到最终编译代码的一对一映射,并让您轻松使用 ctypes 并避免一系列问题。
对于峰值拟合,几乎肯定需要使用数组,这会稍微复杂一些,但仍然可以通过 ctypes 轻松完成。
如果您真的想使用更复杂的数据结构或修改传递的参数,则可以使用 SWIGPython 的标准 C 扩展接口 ,但需要一些麻烦。
对于您正在做的事情,您可能还要查看 NumPy,它可能会执行您想要推送到 C 的某些工作,同时提供一些额外的帮助,以在 Python 和 C 之间移动数据

4

f2py(numpy的一部分)是包装C/Fortran数值计算代码的SWIG和boost.python的更简单的替代方案。


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中定义新的Python类是可能的,但这些事情都很繁琐和冗长,我建议采用上述两种方法之一。

0
Python在允许函数、函数对象和对象作为参数传递给函数和方法方面非常自由,而我怀疑C或Fortran不是这样的。
在C中,您不能将函数作为参数传递给函数,但可以传递函数指针,这与函数一样好。
我不知道当您尝试集成C和Python代码时,这是否有多大帮助,但我只是想澄清一个误解。

0
除了上述工具之外,我还可以推荐使用Pyrex(用于创建Python扩展模块)或Psyco(作为Python的JIT编译器)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接