在 python 中,在什么情况下 SWIG 比 ctypes 更适合调用共享库中的入口点?假设您还没有 SWIG 接口文件。
两者的性能指标是什么?
10 回答
我有丰富的swig使用经验。SWIG 声称它是包装物品的快速解决方案。但在现实生活中...
缺点:
SWIG 被开发为通用的,适用于所有人和 20 多种语言。通常,它会导致缺点:
- 需要配置(SWIG .i 模板),有时很棘手,
- 缺乏对某些特殊情况的处理(进一步参见 python 属性),
- 某些语言缺乏性能。
Python 缺点:
1)代码风格不一致。C++ 和 python 有非常不同的代码风格(这是显而易见的,当然),让目标代码更 Pythonish 的可能性非常有限。例如,从 getter 和 setter 创建属性是很重要的。看到这个问答
2)缺乏广泛的社区。SWIG 有一些很好的文档。但是,如果有人发现文档中没有的内容,则根本没有任何信息。没有博客和谷歌搜索有帮助。所以在这种情况下必须大量挖掘 SWIG 生成的代码......这太可怕了,我可以说......
优点:
在简单的情况下,它确实快速、简单、直接
如果您曾经生成过 swig 接口文件,则可以将此 C++ 代码包装到其他 20 多种语言中的任何一种(!!!)。
SWIG 的一大担忧是性能。由于 2.04 版 SWIG 包含“-builtin”标志,这使得 SWIG 比其他自动包装方式更快。至少一些基准测试表明了这一点。
何时使用 SWIG?
因此,我为自己总结了两种适合使用 swig 的情况:
2) 如果需要为多种语言包装 C++ 代码。或者,如果可能有一段时间需要为多种语言分发代码。在这种情况下,使用 SWIG 是可靠的。
1)如果需要快速包装一些 C++ 库中的几个函数以供最终使用。
现场体验
更新:
一年半过去了,因为我们使用 SWIG 对我们的库进行了转换。
首先,我们制作了一个python版本。有好几次我们在使用 SWIG 时遇到了麻烦——这是真的。但现在我们将库扩展到 Java 和 .NET。所以我们有 3 种语言和 1 个 SWIG。我可以说SWIG在节省大量时间方面非常出色。
更新 2:
我们在这个库中使用 SWIG 已经是两年了。SWIG 已集成到我们的构建系统中。最近我们对 C++ 库的 API 进行了重大更改。SWIG 工作得很好。我们唯一需要做的就是将几个 %rename 添加到 .i 文件中,以便我们CppCamelStyleFunctions()
现在looks_more_pythonish
在 python 中。首先,我担心可能出现的一些问题,但没有出现任何问题。这是惊人的。只需进行几次编辑,所有内容均以 3 种语言分发。现在我相信在我们的案例中使用 SWIG 是一个很好的解决方案。
更新 3:
我们为我们的图书馆使用 SWIG 已经 3 年多了。主要变化:python 部分完全用纯 python 重写。原因是 Python 现在用于我们库的大多数应用程序。即使纯 python 版本的工作速度比 C++ 包装慢,但用户使用纯 python 更方便,而不是与原生库苦苦挣扎。
SWIG 仍用于 .NET 和 Java 版本。
这里的主要问题“如果我们从头开始项目,我们会为 python 使用 SWIG 吗?”。我们会!SWIG 使我们能够将我们的产品快速分发为多种语言。它工作了一段时间,让我们有机会更好地了解我们的用户需求。
SWIG 生成(相当难看的)C 或 C++ 代码。它可以直接用于简单的函数(可以直接翻译的东西),并且相当容易用于更复杂的函数(例如具有输出参数的函数,需要额外的翻译步骤才能在 Python 中表示。)对于更强大的接口,您经常需要将 C 的位写入接口文件的一部分。除了简单的使用之外,您还需要了解 CPython 以及它如何表示对象——并不难,但需要牢记。
ctypes 允许您直接访问 C 函数、结构和其他数据,并加载任意共享库。您不需要为此编写任何 C,但您确实需要了解 C 的工作原理。您可能会说,它是 SWIG 的另一面:它不生成代码,并且在运行时不需要编译器,但除了简单的使用之外,它确实需要您了解诸如 C 数据类型、强制转换、内存管理和对齐工作。您还需要手动或自动将 C 结构、联合和数组转换为等效的 ctypes 数据结构,包括正确的内存布局。
在纯执行中,SWIG 可能比 ctypes 更快——因为围绕实际工作的管理是在编译时在 C 中完成的,而不是在运行时在 Python 中完成的。但是,除非您连接许多不同的 C 函数,但每个函数只连接几次,否则开销不太可能真的很明显。
在开发时,ctypes 的启动成本要低得多:您不必了解接口文件,不必生成 .c 文件并编译它们,您不必检查并消除警告。您可以轻松地开始使用单个 C 函数,然后将其扩展为更多。您可以直接在 Python 解释器中进行测试和尝试。包装大量代码有点乏味,尽管有一些尝试让它变得更简单(比如 ctypes-configure。)
另一方面,SWIG 可用于为多种语言生成包装器(除非需要填写特定于语言的细节,例如我上面提到的自定义 C 代码。)当包装大量 SWIG 可以处理的代码时帮助,代码生成也可以比 ctypes 等效设置简单得多。
CTypes 非常酷并且比 SWIG 容易得多,但它的缺点是编写不佳或恶意编写的 python 代码实际上可能会使 python 进程崩溃。您还应该考虑boost python。恕我直言,它实际上比 swig 更容易,同时让您可以更好地控制最终的 python 界面。如果您无论如何都在使用 C++,那么您也不会在您的组合中添加任何其他语言。
根据我的经验,ctypes 确实有一个很大的缺点:当出现问题时(对于任何复杂的接口都会出现问题),调试起来简直就是地狱。
问题是你的堆栈的很大一部分被 ctypes/ffi 魔法遮住了,没有简单的方法来确定你是如何到达特定点的,以及为什么参数值是它们的值。
ctypes 很棒,但不处理 C++ 类。我还发现 ctypes 比直接 C 绑定慢约 10%,但这在很大程度上取决于您所调用的内容。
如果您打算使用 ctypes,请务必查看 Pyglet 和 Pyopengl 项目,它们有大量 ctype 绑定示例。
我将逆向建议,如果可以的话,你应该使用标准的 Python API来编写你的扩展库。从 C 和 Python 的角度来看,它确实很好地集成了……如果您对 Perl API 有任何经验,您会发现它非常令人惊喜。
Ctypes 也很好,但正如其他人所说,它不支持 C++。
您要包装的图书馆有多大?代码库的变化有多快?还有其他维护问题吗?这些都可能会影响编写 Python 绑定的最佳方式的选择。
只是想添加一些我还没有看到的注意事项。[编辑:糟糕,没有看到 Mike Steder 的回答]
如果您想尝试使用非 Cpython 实现(如 PyPy、IronPython 或 Jython),那么 ctypes 是唯一的方法。PyPy 不允许编写 C 扩展,因此排除了 pyrex/cython 和 Boost.python。出于同样的原因,ctypes 是唯一适用于 IronPython 和(最终,一旦它们全部工作)jython 的机制。
正如其他人提到的,不需要编译。这意味着如果 .dll 或 .so 的新版本出现,您只需将其放入并加载该新版本。只要没有任何接口发生变化,它就是替代品。
需要记住的是,SWIG 仅针对 CPython 实现。由于 PyPy 和 IronPython 实现也支持 ctypes,因此可能值得使用 ctypes 编写模块以与更广泛的 Python 生态系统兼容。
我发现 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 之后,这不是 t如此跨平台)。我还可以轻松地将 PyGGI 部署到任何支持 GTK 的平台。