22

一段时间以来,我一直在考虑编写一个峰值拟合库。我对 Python 非常了解,并计划一开始就用 Python 实现一切,但设想我最终可能不得不用编译语言重新实现一些核心例程。

IIRC,Python 最初的职责之一是作为原型语言,但是 Python 在允许将函数、函子、对象传递给函数和方法方面非常自由,而我怀疑 C 或 Fortran 并非如此。

关于设计我设想必须与编译语言接口的函数/类,我应该知道什么?这些潜在问题中有多少是由 cTypes、bgen、SWIGBoost.PythonCythonPython SIP等库处理的?

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

4

7 回答 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 中快速使用新库的好方法。我可以推荐它来连接外部库。

Boost.Python:非常愉快。如果你已经有自己的 C++ 代码想要在 python 中使用,那就去吧。通过这种方式将 c++ 类结构转换为 python 类结构非常容易。如果你在 python 中有需要的 c++ 代码,我推荐它。

Pyrex/Cython:使用 Cython,而不是 Pyrex。时期。Cython 更先进,使用起来更愉快。如今,我用 cython 做所有我过去用 SWIG 或 Ctypes 做的事情。如果您的 python 代码运行速度太慢,这也是最好的方法。这个过程非常棒:你将你的 python 模块转换成 cython 模块,构建它们并像它仍然是 python 一样继续分析和优化(不需要更改工具)。然后,您可以将尽可能多(或尽可能少)的 C 代码与您的 python 代码混合使用。这比用 C 重写应用程序的整个部分要快得多;你只重写内部循环。

时序:ctypes 的调用开销最高(~700ns),其次是 boost.python(322ns),然后是 swig(290ns)。Cython 具有最低的调用开销 (124ns) 和最好的反馈(cProfile 支持!)。这些数字来自我的盒子,它调用了一个从交互式外壳返回整数的简单函数;因此,模块导入开销不是定时的,只有函数调用开销是定时的。因此,通过分析和使用 cython 来快速获取 python 代码是最简单和最高效的。

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


编辑:我忘了提:出于数字目的(即连接到 NumPy)使用 Cython;他们对此有支持(因为他们基本上为此目的开发了cython)。所以这应该是您决定的另一个+1。

于 2009-11-02T13:16:20.447 回答
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 对象)。然后,您可以按如下方式使用此函数(假设一元位于被调用的图像中,该图像还包含 Image 和加载函数):

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

至于使用带有 boost 的数组,我个人没有这样做,但我知道使用 boost 将数组公开给 python 的功能是可用的——可能会有所帮助。

于 2008-08-26T15:58:08.880 回答
6

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

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

对于峰值拟合,您几乎肯定需要使用数组,这会使事情变得有点复杂,但对于 ctypes 仍然非常可行。

如果你真的想使用更复杂的数据结构,或者修改传递的参数,SWIGPython 的标准 C 扩展接口将让你做你想做的事,但有一些麻烦。

对于您正在做的事情,您可能还想查看NumPy,它可能会完成一些您想要推送到 C 的工作,并在 Python 和 C 之间来回移动数据提供一些额外的帮助

于 2008-08-20T01:45:05.390 回答
4

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

于 2008-09-29T22:30:59.973 回答
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 类,但这些事情既烦人又冗长,我建议采用上述两种方法之一。

于 2008-08-19T13:52:23.033 回答
0

Python 在允许将函数、函子、对象传递给函数和方法方面非常自由,而我怀疑 C 或 Fortran 并非如此。

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

我不知道当您尝试集成 C 和 Python 代码时这会有多大帮助,但我只是想澄清一个误解。

于 2008-09-29T22:52:19.580 回答
0

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

于 2009-05-16T15:08:53.677 回答