Python:SWIG vs ctypes

63

在Python中,什么情况下SWIG比ctypes更适合调用共享库中的入口点?假设您没有SWIG接口文件。

这两者的性能指标是什么?


1
你为什么不考虑使用Pyrex/Cython? - Thomas Wouters
3
没问题,工作中的辩论是关于SWIG和ctypes的比较! :) - Kevin Little
2
现在你有了新的武器,你可以考虑使用Pyrex/Cython。它是ctypes和SWIG之间的中间地带:编译时像Python一样。 - Thomas Wouters
10个回答

100

我有丰富的使用swig的经验。SWIG声称它是一种快速解决方案来包装各种东西。但在实际应用中...


缺点:

SWIG开发的目标是通用性,适用于大多数人和20多种编程语言。这通常会导致以下缺点:
- 需要配置(SWIG .i 模板),有时很棘手,
- 没有处理某些特殊情况(请参见下文有关Python属性的说明),
- 对于某些语言而言性能不足。

Python缺点:

1)代码风格不一致。 C++ 和 Python 的代码风格非常不同(这显然是肯定的),因此 Swig 使目标代码更像 Python 的可能性非常有限。例如,从 getter 和 setter 创建属性是很困难的。详见 该问题和答案。

2)缺乏广泛的社区支持。 SWIG 有一些良好的文档。但如果遇到文档中没有涉及的问题,就找不到任何信息了。没有博客也无法通过谷歌获得帮助。在这种情况下,必须大量挖掘 SWIG 生成的代码... 真是可怕,我可以这样说...

优点:

  • 在简单情况下,它真的很快速、简单和直接

  • 如果您一旦生成了 swig 接口文件,就可以将此 C++ 代码包装到其他20多种语言中!

  • SWIG 的一个大问题是性能。自版本 2.04 起,SWIG 包括“-builtin”标志,使 SWIG 比其他自动包装方式更快。至少一些 基准测试 显示了这一点。


何时使用 SWIG?

所以我得出结论,在以下两种情况下使用 swig 是不错的选择:

2) 如果需要将C++代码封装为多种语言或者可能需要将代码分发到多种语言中,使用SWIG是可靠的。

1) 如果只需要快速地封装一些来自某个C++库的函数以供最终使用,则可以使用SWIG。


实时经验

更新:
我们使用SWIG转换我们的库已经过去了一年半。

首先,我们做了一个Python版本。在使用SWIG的过程中有几次遇到麻烦 - 这是真的。但是现在我们将库扩展到了Java和.NET。所以我们有了1个SWIG带着3种语言。我可以说在节省大量时间方面,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版本。

这里的主要问题是“如果我们从一开始就启动项目,是否会使用SWIG来编写Python代码?” 我们会! SWIG使我们能够快速将产品分发到许多语言中。它在一段时间内运作良好,为我们提供了更好地了解用户需求的机会。


9
特别感谢更新部分,并在一年半后提供真实的项目经验! - frank28_nfls
如果实际上有很好的文档但是“没有谷歌帮助”,那么可能将“缺乏文档”标题改为“缺乏广泛社区”更为合适。 - n611x007
谢谢。我完全同意,SWIG非常棒。我甚至开始使用不同的后端来改进SWIG。在示例方面,我发现最有用的地方是NumPy和当然还有XBMC。 - Jens Munk

71

SWIG生成(相当丑陋的)C或C++代码。对于简单函数(可以直接转换的内容),使用很简单,对于更复杂的函数(例如需要额外翻译步骤才能在Python中表示输出参数的函数),使用也相对容易。但是,对于更强大的交互,您通常需要编写C代码作为接口文件的一部分。除非使用简单功能,否则您需要了解CPython以及它如何表示对象--不难,但需要牢记。

ctypes允许您直接访问C函数、结构和其他数据,并加载任意共享库。这不需要编写任何C代码,但必须了解C语言的工作原理。可以这样说,它是SWIG的反面:不生成代码,运行时不需要编译器,但对于除简单用途之外的所有用途,确实需要了解诸如C数据类型、转换、内存管理和对齐方式的工作原理。您还需要手动或自动将C结构体、联合和数组转换为等效的ctypes数据结构,包括正确的内存布局。

在纯执行方面,SWIG可能比ctypes快--因为与Python运行时相比,编译时使用C来管理实际工作的开销较小。然而,除非您接口了许多不同的C函数,但每个都只使用几次,否则开销不太明显。

在开发时间方面,ctypes的启动成本要低得多:您不需要了解接口文件,也不需要生成.c文件并将其编译,也不需要检查和消除警告。您可以轻松地开始使用单个C函数,然后将其扩展到更多内容。而且,您可以直接在Python解释器中测试和尝试事物。包装大量代码有点乏味,尽管有尝试使其更简单的方法(例如ctypes-configure)。

另一方面,SWIG可用于为多种语言生成包装器(除了需要填充自定义C代码等语言特定细节之外)。如果要包装许多SWIG可以很容易处理的代码,则代码生成也比ctypes等效项简单得多。


2
在使用SWIG时遇到了困难,然后看到了这个答案。它让我相信应该转向使用CTypes。[为什么我没想到先去stackoverflow上找呢 ;-)]可以在以下链接中找到一个很好的概述:http://www.slideshare.net/gnunify/c-types-extending-python - CyberFonic
2
我更喜欢CTypes,因为它完全避免了编译过程。这在编写可能在多个平台上使用的模块时特别有优势,尤其是那些没有方便访问编译器的平台(比如64位Windows)。CTypes还与Python版本无关。也就是说,你可以编写一个ctypes接口,在Python 2.4、2.6和3.1下都能正常工作,而无需进行修改。 - Jason R. Coombs
3
我完全不同意,我是SWIG的铁杆粉丝。一旦你了解了如何创建类型映射,例如使用NumPy提供的类型映射,你就不需要担心因接口更改、内存对齐问题、索引错误、垃圾收集等问题而导致的分段错误。 - Jens Munk
你能否提供一些解释,为什么由swig生成的C++代码“相当丑陋”? - BiA

14

CTypes非常酷,比SWIG要简单得多,但它的缺点是糟糕或恶意编写的Python代码实际上可能会使Python进程崩溃。您还应该考虑使用boost python。在我看来,它比swig更容易使用,同时还能让您对最终的Python接口拥有更多控制权。如果您已经在使用C++,那么您也不需要添加其他语言到您的混合中。


2
哦哦哦!我不知道的新东西——感谢指向Boost.Python的指针!! - Kevin Little

11
在我的经验中,ctypes确实有一个巨大的缺点:当出现问题时(对于任何复杂接口都会发生),调试起来非常困难。
问题在于,ctypes/ffi的一大部分堆栈被隐藏了起来,而且很难确定你到达某个特定点的方式以及为什么参数值是什么。

我同意,如果你搞错了参数或索引,事情就会出问题。没有类型安全性。 - Jens Munk

8
你还可以使用Pyrex,它可以作为高级Python代码和低级C代码之间的粘合剂。例如,lxml就是用Pyrex编写的。

7
Cython 是一种语言,使得编写 Python 语言的 C 扩展与编写 Python 一样简单。Cython 基于著名的 Pyrex,但支持更多先进的功能和优化。 - mtasic85

7

ctypes很好用,但不能处理C++类。我也发现ctypes比直接使用C绑定慢了约10%,但这将高度取决于您调用的内容。

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


7
我会持异议并建议,如果可能的话,您应该使用标准Python API编写您的扩展库。从C和Python的角度来看,它非常好地集成在一起...如果您有使用Perl API的经验,您会发现这是一个非常愉快的惊喜。
Ctypes也很好,但正如其他人所说,它不支持C++。
您要包装的库有多大?代码库变化有多快?还有其他维护问题吗?这些都可能影响编写Python绑定的最佳方式的选择。

2
@Dan,我正在处理的库是第三方的--例如VMware的VIX API。我别无选择,只能尽力使用它们。在可能的情况下,我会尽量使用标准的Python API,相信我! :) - Kevin Little

5

我想补充一些尚未提到的考虑因素。 [编辑:哎呀,没看到Mike Steder的回答]

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

正如其他人提到的,不需要编译。这意味着,如果新版本的.dll或.so发布,您只需将其放入并加载该新版本。只要没有更改任何接口,它就是一个无缝替换。


3

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


1
我发现SWIG在其方法上有些臃肿(不仅仅是Python),并且难以实现,而不必写出显式的Python代码来适应SWIG,而是编写干净的Python代码。如果使用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的任何平台。

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

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