编译之前使用Python代码进行原型制作

时间:2020-03-05 18:40:42  来源:igfitidea点击:

一段时间以来,我一直在考虑编写峰拟合库。我对Python相当了解,并计划从Python开始实现所有功能,但设想最终我可能必须以编译语言重新实现一些核心例程。

IIRC是Python的原始语言之一,它是一种原型语言,但是Python在允许将函数,函子,对象传递给函数和方法方面相当自由,而我怀疑C或者Fortran并非如此。

我对设计将要与编译语言进行接口的函数/类有什么了解?这些潜在问题中有多少是由cTypes,bgen,SWIG,Boost.Python,Cython或者Python SIP之类的库处理的?

对于这个特殊的用例,(拟合库)我想让用户将数学函数(Guassian,Lorentzian等)定义为Python函数,然后可以将其传递给已编译的代码拟合库进行解释。传递和返回数组也是必不可少的。

解决方案

回答

以我的经验,有两种简单的方法可以从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类,但这些事情令人烦恼且冗长,我建议我们采用上述两种方法之一。

回答

计划最终过渡到已编译代码的最佳方法是将性能敏感部分编写为功能样式(无状态且无副作用)的简单函数模块,该样式接受并返回基本数据类型。

这将提供从Python原型代码到最终编译代码的一对一映射,并使我们轻松使用ctypes并避免一堆麻烦。

对于峰拟合,我们几乎肯定会需要使用数组,这会使事情复杂一些,但对于ctypes仍然非常可行。

如果我们确实想使用更复杂的数据结构,或者修改传递的参数,SWIG或者Python的标准C扩展接口将使我们可以执行所需的操作,但会有些麻烦。

对于我们正在做的事情,我们可能还想看看NumPy,它可以完成我们想要推送到C的一些工作,并提供一些其他帮助来在Python和C之间来回移动数据。

回答

我没有使用过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对象)。然后,我们可以按以下方式使用此函数(假设一元位于所调用的映像中,该映像还包含Image和load函数):

import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)

至于将数组与boost一起使用,我个人还没有做过,但是我知道使用boost将数组暴露给python的功能是可用的,这可能会有所帮助。

回答

f2py(" numpy"的一部分)是SWIG和boost.python的更简单替代方案,用于包装C / Fortran数字运算代码。

回答

Python is pretty liberal in allowing functions, functors, objects to be passed to functions and methods, whereas I suspect the same is not true of say C or Fortran.

在C语言中,我们不能将函数作为参数传递给函数,但可以传递与函数一样好的函数指针。

我不知道当我们尝试集成C和Python代码时有什么帮助,但是我只是想消除一个误解。

回答

除了上述工具外,我建议使用Pyrex

回答

(用于创建Python扩展模块)或者Psyco(作为用于Python的JIT编译器)。

最后一个问题,我真的可以给:)一个有价值的答案。

我为我的工作(光学测量技术博士学位)研究了f2py,boost.python,swig,cython和pyrex。我广泛使用swig,一些使用boost.python,很多使用pyrex和cython。我还使用了ctypes。这是我的细分:

免责声明:这是我的亲身经历。我没有参与任何这些项目。

wig
在C ++中不能很好地发挥作用。可以,但是在Linux和Mac OS X上,链接步骤中的名称修改问题令我感到头疼。如果我们拥有C代码并希望将其连接到python,那么这是一个不错的解决方案。我为自己的需要包装了GTS,并且基本上需要编写一个可以连接到的C共享库。我不推荐它。

Ctypes:
我使用ctypes编写了一个libdc1394(IEEE相机库)包装,这是一个非常精明的经验。我们可以在https://launchpad.net/pydc1394上找到该代码。将标头转换为python代码需要做很多工作,但是随后一切都可以可靠地工作。如果要连接外部库,这是一个好方法。 Ctypes也位于python的stdlib中,因此每个人都可以立即使用代码。这也是快速使用python中的新库的一种好方法。我可以推荐它连接到外部库。

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),然后是直接的wig(290ns)。 Cython的通话开销最低(124ns),而花时间的最佳反馈(cProfile支持!)。这些数字从我的盒子中调用了一个琐碎的函数,该函数从交互式外壳返回一个整数;因此,模块导入开销不是定时的,只有函数调用开销是定时的。因此,通过分析和使用cython来快速获取python代码是最简单,最有效的方法。

摘要:对于问题,请使用Cython;)。我希望此摘要对某些人有用。我很乐意回答任何剩余的问题。

段落数量不匹配