Tiobe上,Python长期霸榜编程语言第一,但从我的角度来说,Python其实就是个渣渣,它完全是靠给C/C++当做胶水,才能活成现在如此滋润的地步。几十年来,积累下来的C/C++库,一旦套上了Python的皮,就显得眉清目秀了!
那么,到底如何用Python给C/C++当胶水呢?
Python调用C/C++,市面上能用的路子其实就那么几条:
ctypes、cffi、还有pybind11和nanobind这种专门给C++用的绑定库。
ctypes与cffi:站在C这一侧
这里先说说ctypes和cffi的调用方法。
ctypes和cffi都属于FFI(Foreign Function Interface)方案,核心思路是在Python这边加载编译好的动态链接库,然后直接调用里面的函数。它们的视角是站在Python这一侧,向C世界发起调用。
但具体怎么实现这个调用,两者的设计哲学有明显的差异。
ctypes:标准库的朴素方案
ctypes是Python标准库的一部分,优点是不需要安装任何第三方包。
它的做法很直接:用CDLL("./mylib.dll")加载库,然后通过设置argtypes和restype来声明函数的参数和返回值类型。
比如,下面这里有一个dll,要通过ctypes和cffi来调用:


下面是一段代码来演示各种常见操作,传结构体指针,再到把C++对象伪装成不透明指针传来传去,进行dll的调用:


强烈提醒,上面ctypes库的使用要用这种相对路径的方式:CDLL("./mylib.dll"),否则出错。
ctypes的好处是零额外依赖,任何一个Python环境都能用。
代价是每个函数都需要单独声明argtypes和restype,接口多起来之后代码会显得比较啰嗦。而且ctypes本质上是和C的二进制接口(ABI)打交道,如果结构体的内存布局对不上,运行时可能会出一些难以追踪的问题。
cffi:来自LuaJIT的设计
cffi和ctypes最大的不同,在于它的设计直接继承了LuaJIT的FFI。
这是LuaJIT的一个标志性特性,在Lua社区里以“性能好、用起来自然”著称。
cffi的作者Armin Rigo和Maciej Fijalkowski在设计之初就明确提到了这一点:cffi的接口是基于LuaJIT's FFI的,遵循几个核心原则。
cffi的做法是让用户用C语言的语法来声明要调用的接口,然后由cffi来处理剩下的转换工作:


强烈提醒,与前面ctypes库不一样,这里cffi库的使用要用这种方式:ffi.dlopen("mylib.dll"),否则出错。
对比ctypes的写法,cffi把接口声明集中在一段C代码里,直接从头文件复制粘贴过来就能用,不需要像ctypes那样逐个函数设置argtypes。
这背后反映了cffi的设计目标:让用户只懂C和Python,不需要学第三种语言或一套新的API。
对比来看,ctypes的写法更Pythonic,但每个函数都要单独声明argtypes和restype,接口多了之后代码会显得很啰嗦。
cffi把声明集中在一起,读起来更像C头文件,但需要额外安装cffi包。
性能上两者都属于FFI(外部函数接口)方案,调用C函数时都需要做类型转换和数据编组。有测试数据显示ctypes的调用开销在100纳秒到几微秒这个量级,cffi略快一些,但差异在实际项目中通常感知不到。
真正麻烦的是处理C++——这两套方案都是为C设计的,想调用C++的类?得自己在C层写一堆包装函数,把对象变成void*指针传来传去,既丑陋又容易出错。
ctypes适合的场景是:你有一个现成的C库,想快速在Python里用起来,而且不想给项目增加额外依赖。
cffi则是在你需要更安全、更规范的接口声明时的选择,尤其如果你在用PyPy,cffi几乎是官方推荐。
cffi与LuaJIT FFI的关系
cffi和LuaJIT FFI的关系值得多说两句。
LuaJIT的FFI是在Lua社区公认的“杀手级特性”,它让Lua可以直接调用C函数,而且性能非常接近手写的C扩展。
cffi在设计上高度模仿了LuaJIT FFI的API风格——比如ffi.cdef()声明接口、ffi.new()分配C结构体内存、ffi.string()转换C字符串——这些用法在LuaJIT FFI里都能找到对应的影子。
但cffi并不是简单的移植。它在LuaJIT FFI的基础上增加了Python特有的集成——比如和PyPy的深度配合、对Python C API的兼容性处理等。cffi的作者之一Armin Rigo同时也是PyPy的核心开发者,这也解释了为什么cffi对PyPy的支持比ctypes好得多。
所以,对于一个熟悉Luajit的人来说,当然是强烈建议使用cffi了。一套方案,同时适用于lua和python,想想就开心。