Python:SWIG 与 ctypes

Posted

技术标签:

【中文标题】Python:SWIG 与 ctypes【英文标题】:Python: SWIG vs ctypes 【发布时间】:2010-09-13 05:52:41 【问题描述】:

在 python 中,在什么情况下 SWIG 比 ctypes 更适合调用共享库中的入口点?假设您还没有 SWIG 接口文件。 两者的性能指标是什么?

【问题讨论】:

您没有考虑 Pyrex/Cython 的任何特殊原因? 嗯,是的——工作中的争论是关于 SWIG 与 ctypes 的! :) 好吧,现在你有了新弹药,你也可以考虑 Pyrex/Cython。它是 ctypes 和 SWIG 之间的中间地带:编译时但类似于 python。 【参考方案1】:

我有丰富的 swig 使用经验。 SWIG 声称它是包装物品的快速解决方案。但在现实生活中……


缺点:

SWIG 被开发为通用的,适用于所有人和 20 多种语言。通常,它会导致缺点: - 需要配置(SWIG .i 模板),有时很棘手, - 缺乏对某些特殊情况的处理(进一步参见 python 属性), - 某些语言缺乏性能。

Python 的缺点:

1) 代码风格不一致。 C++ 和 python 有非常不同的代码风格(这是显而易见的,当然),让目标代码更 Pythonish 的可能性非常有限。例如,从 getter 和 setter 创建属性是很重要的。见this q&a

2) 缺乏广泛的社区。 SWIG 有一些很好的文档。但是,如果有人发现文档中没有的内容,则根本没有任何信息。没有博客和谷歌搜索有帮助。所以在这种情况下必须大量挖掘 SWIG 生成的代码......这太糟糕了,我可以说......

优点:

在简单的情况下,它确实快速、简单、直接

如果您曾经生成过 swig 接口文件,则可以将此 C++ 代码包装到其他 20 多种语言中的任何一种 (!!!)。

关于 SWIG 的一大担忧是性能。由于 2.04 版 SWIG 包含“-builtin”标志,这使得 SWIG 比其他自动包装方式更快。至少some benchmarks 显示了这一点。


什么时候使用 SWIG?

所以我给自己总结了两个swig好用的案例:

2) 如果需要为多种语言包装 C++ 代码。或者,如果可能有一段时间需要为多种语言分发代码。在这种情况下,使用 SWIG 是可靠的。

1) 如果需要快速从某个 C++ 库中包装几个函数以供最终使用。


现场体验

更新: 一年半过去了,我们使用 SWIG 对我们的库进行了转换。

首先,我们制作了一个 python 版本。有好几次我们在使用 SWIG 时遇到了麻烦——这是真的。但现在我们将库扩展到 Java 和 .NET。所以我们有 3 种语言和 1 个 SWIG。我可以说 SWIG 摇滚 在节省大量时间方面。

更新 2: 我们为这个库使用 SWIG 已经两年了。 SWIG 已集成到我们的构建系统中。最近我们对 C++ 库的 API 进行了重大更改。 SWIG 工作得很好。我们唯一需要做的就是在 .i 文件中添加几个 %rename ,这样我们的 CppCamelStyleFunctions() 现在就可以在 python 中使用了 looks_more_pythonish 了。首先,我担心可能出现的一些问题,但没有出现任何问题。这是惊人的。只需进行几次编辑,所有内容均以 3 种语言分发。现在我相信在我们的案例中使用 SWIG 是一个很好的解决方案。

更新 3: 我们为我们的图书馆使用 SWIG 已经 3 年多了。 重大变化:python 部分完全用纯 python 重写。原因是 Python 现在用于我们库的大多数应用程序。即使纯 python 版本的工作速度比 C++ 包装慢,但用户使用纯 python 更方便,而不是与原生库苦苦挣扎。

SWIG 仍用于 .NET 和 Java 版本。

这里的主要问题“如果我们从头开始项目,我们会为 python 使用 SWIG 吗?”。我们会! SWIG 使我们能够将我们的产品快速分发为多种语言。它工作了一段时间,让我们有机会更好地了解我们的用户需求。

【讨论】:

特别感谢一年半后有真实项目经验的Update部分! 如果确实有很好的文档但“谷歌搜索没有帮助”,则将“缺乏文档”标题称为“缺乏广泛的社区”可能相当重要 谢谢。我完全同意,SWIG 摇滚。我什至开始使用不同的后端改进 SWIG。我发现在示例方面最有用的地方是 NumPy,当然还有 XBMC。【参考方案2】:

SWIG 生成(相当难看的)C 或 C++ 代码。它可以直接用于简单的函数(可以直接翻译的东西),并且相当容易用于更复杂的函数(例如具有输出参数的函数,需要额外的翻译步骤才能在 Python 中表示。)对于更强大的接口,您经常需要将 C 的位写入接口文件的一部分。除了简单的使用之外,您还需要了解 CPython 以及它如何表示对象——并不难,但需要牢记。

ctypes 允许您直接访问 C 函数、结构和其他数据,并加载任意共享库。您不需要为此编写任何 C,但您确实需要了解 C 的工作原理。你可能会争辩说,它是 SWIG 的另一面:它不生成代码,并且在运行时不需要编译器,但除了简单的使用之外,它确实需要你了解 C 数据类型、强制转换、内存管理和对齐工作。您还需要手动或自动将 C 结构、联合和数组转换为等效的 ctypes 数据结构,包括正确的内存布局。

在纯执行中,SWIG 可能比 ctypes 更快——因为围绕实际工作的管理是在编译时在 C 中而不是在运行时在 Python 中完成的。但是,除非您接口很多不同的 C 函数,但每个函数只连接几次,否则开销不太可能真的很明显。

在开发的时候,ctypes的启动成本要低很多:不用学习接口文件,不用生成.c文件并编译,不用check out和silence警告。您可以轻松地开始使用单个 C 函数,然后将其扩展为更多。您可以直接在 Python 解释器中进行测试和尝试。包装大量代码有点乏味,尽管有人尝试让它变得更简单(如 ctypes-configure。)

另一方面,SWIG 可用于为多种语言生成包装器(除非需要填写特定于语言的详细信息,例如我上面提到的自定义 C 代码。)当包装大量 SWIG 可以使用的代码时无需帮助即可处理,代码生成的设置也比 ctypes 等效项要简单得多。

【讨论】:

在 SWIG 上苦苦挣扎,发现了这个答案。它说服我改用 CTypes。 [为什么我没有想到先看看 *** ;-)] 可以在以下位置找到一个很好的概述:slideshare.net/gnunify/c-types-extending-python 我更喜欢 CTypes,因为它完全避免了编译。这在编写可能在多个平台上使用的模块时特别有利,尤其是那些不容易访问编译器的模块(例如 64 位 Windows)。 CTypes 也与 python 版本无关。也就是你可以写一个ctypes接口,不加修改就可以在Python 2.4、2.6、3.1下工作。 我完全不同意,我是 SWIG 的忠实粉丝。一旦你了解了类型映射是如何创建的,例如使用 NumPy 提供的类型映射,您无需担心由于接口更改、内存对齐问题、索引错误、垃圾收集等导致的分段错误。 你能解释一下为什么 swig 生成的 C++ “相当难看”吗?【参考方案3】:

CTypes 非常酷并且比 SWIG 容易得多,但它的缺点是编写不佳或恶意编写的 Python 代码实际上会使 Python 进程崩溃。您还应该考虑boost python。恕我直言,它实际上比 swig 更容易,同时让您可以更好地控制最终的 python 界面。如果你仍然使用 C++,你也不要在你的组合中添加任何其他语言。

【讨论】:

噢噢噢!我不知道的闪亮的新事物——感谢指向 Boost.Python 的指针!【参考方案4】:

根据我的经验,ctypes 确实有一个很大的缺点:当出现问题时(对于任何复杂的接口都会出现问题),调试起来简直就是地狱。

问题是你的堆栈的很大一部分被 ctypes/ffi 魔法所掩盖,并且没有简单的方法来确定你是如何到达特定点的以及为什么参数值是它们的值..

【讨论】:

我同意,如果你搞砸了你的论点或索引,事情就会爆炸。没有类型安全【参考方案5】:

您还可以使用Pyrex,它可以充当高级 Python 代码和低级 C 代码之间的粘合剂。例如,lxml 是用 Pyrex 编写的。

【讨论】:

“Cython 是一种语言,它使为 Python 语言编写 C 扩展就像 Python 本身一样简单。Cython 基于著名的 Pyrex,但支持更先进的功能和优化。”跨度> 【参考方案6】:

ctypes 很棒,但不处理 C++ 类。我还发现 ctypes 比直接 C 绑定慢约 10%,但这在很大程度上取决于您所调用的内容。

如果您打算使用 ctypes,请务必查看 Pyglet 和 Pyopengl 项目,它们有大量 ctype 绑定示例。

【讨论】:

【参考方案7】:

我将逆向建议,如果可以的话,您应该使用standard Python API 编写您的扩展库。从 C 和 Python 的角度来看,它确实很好地集成了...如果您对 Perl API 有任何经验,您会发现它非常令人惊喜。

Ctypes 也不错,但正如其他人所说,它不支持 C++。

您要包装的库有多大?代码库的变化有多快?还有其他维护问题吗?这些都可能会影响编写 Python 绑定的最佳方式的选择。

【讨论】:

@Dan,我处理的库是第三方库——例如 VMware 的 VIX API。我别无选择,只能尽我所能使用它们。我尽可能使用标准的 Python API,相信我! :)【参考方案8】:

只是想补充一些我还没有提到的注意事项。 [编辑:糟糕,没有看到 Mike Steder 的回答]

如果您想尝试使用非 Cpython 实现(如 PyPy、IronPython 或 Jython),那么 ctypes 是唯一可行的方法。 PyPy 不允许编写 C 扩展,因此排除了 pyrex/cython 和 Boost.python。出于同样的原因,ctypes 是唯一适用于 IronPython 和(最终,一旦它们全部工作)jython 的机制。

正如其他人所说,不需要编译。这意味着,如果 .dll 或 .so 的新版本出现,您只需将其放入并加载该新版本即可。只要没有任何接口发生变化,它就是替代品。

【讨论】:

【参考方案9】:

要记住的是,SWIG 仅针对 CPython 实现。由于 PyPy 和 IronPython 实现也支持 ctypes,因此可能值得使用 ctypes 编写模块以与更广泛的 Python 生态系统兼容。

【讨论】:

【参考方案10】:

我发现 SWIG 的方法有点臃肿(一般来说,不仅仅是 Python),而且很难实现,而不必跨越以明确的心态编写 Python 代码以对 SWIG 友好,而不是编写干净、编写良好的 Python 代码。恕我直言,将 C 绑定编写到 C++(如果使用 C++)然后使用 ctypes 连接到任何 C 层,这是一个更直接的过程。

如果您要连接的库具有作为库一部分的 C 接口,ctypes 的另一个优点是您不必编译单独的 python 绑定库来访问第三方库。这在制定避免跨平台编译问题的纯 python 解决方案时特别好(对于那些在不同平台上提供的第三方库)。必须以跨平台友好的方式将编译后的代码嵌入到您希望部署在 PyPi 之类的东西上的包中,这很痛苦。关于使用 SWIG 或底层显式 C 代码的 Python 包,我最恼火的一点是它们通常无法跨平台使用。因此,如果您正在使用跨平台可用的第三方库并围绕它们开发 Python 解决方案,请考虑这一点。

作为一个真实的例子,考虑 PyGTK。这(我相信)使用 SWIG 生成 C 代码以连接 GTK C 调用。我在最短的时间内使用它只是发现设置和使用它真的很痛苦,如果你在设置时没有按照正确的顺序做事情,就会出现奇怪的奇怪错误,而且只是一般情况下。这是一次令人沮丧的经历,当我在网上查看 GTK 提供的接口定义时,我意识到将这些接口编写为 python ctypes 接口的翻译器是多么简单的练习。一个名为 PyGGI 的项目诞生了,有一天我能够将 PyGTK 重写为一个功能更强大、更有用的产品,它与 GTK C 面向对象的接口完全匹配。而且它不需要编译 C 代码,使其跨平台友好。 (我实际上是在连接到 webkitgtk 之后,它不是那么跨平台)。我还可以轻松地将 PyGGI 部署到任何支持 GTK 的平台。

【讨论】:

你只需要一些耐心。例如。 SWIG 不递归标头,这意味着您的接口必须编写得很好。我现在使用 SWIG 和一个通用类型库编写了大约 15 个库。当涉及到重构和维护以前的接口直到弃用时,它非常灵活。我的建议是使用 NumPy.i 查找一些示例

以上是关于Python:SWIG 与 ctypes的主要内容,如果未能解决你的问题,请参考以下文章

尝试获取一个 C 库并使用 swig 将其公开给 Windows 中的 Python。关于静态与动态以及使用 Visual Studio for swig 的问题

swig 在 C++ 中运行的结果与在 python 中的不同

SWIG 和 Mingw-64 与 Python 一起使用……不是有效的 Win32 应用程序

使用 SWIG 绑定 Python/C++ 模板

相当于存储 boost::python::object 的 SWIG

在C ++中运行swig的结果与在python中运行的结果不同