2.4.1 在Python中调用C/C++函数的原理
由于Python解释器是由C语言实现的,因此在Python中可以实现C和C++函数的调用。现代机器学习框架(包括TensorFlow、PyTorch和MindSpore)主要依赖Pybind11将底层的大量C和C++函数自动生成对应的Python函数,这一过程一般称为Python绑定(Binding)。在Pybind11出现以前,将C和C++函数进行Python绑定的手段主要包括:
(1)使用Python的C-API。这种方式要求在一个C++程序中包含Python.h,并使用Python的C-API对Python语言进行操作。使用这套API需要对Python的底层实现有一定了解,比如如何管理引用计数等,具有较高的使用门槛。
(2)使用简单包装界面产生器(Simplified Wrapper and Interface Generator,SWIG)。SWIG可以将C和C++代码暴露给Python。SWIG是TensorFlow早期使用的方式。这种方式需要用户编写一个复杂的SWIG接口声明文件,并使用SWIG自动生成使用Python C-API的C代码。自动生成的代码可读性很低,因此代码维护开销大。
(3)使用Python的ctypes模块。该模块提供了C语言中的类型以及直接调用动态链接库的能力,但其缺点是依赖C的原生类型,对自定义类型支持不好。
(4)使用CPython。CPython是结合了Python和C语言的一种语言,可以简单地认为它就是给Python加上了静态类型后的语法,使用者可以维持大部分的Python语法。CPython编写的函数会被自动转译为C和C++代码,因此在CPython中可以插入对于C/C++函数的调用。
(5)使用Boost::Python。它是一个C++库,可以将C++函数转换为Python函数。其原理和Python C-API类似,但是使用方法更简单。由于引入了Boost库,因此有过多的第三方依赖。
相对于上述的Python绑定的手段,Pybind11具有类似于Boost::Python方法的简洁性和易用性,但由于它支持C++11,并且去除Boost依赖,因此成为了轻量级的Python库,特别适合在一个复杂的C++项目(例如本书讨论的机器学习系统)中生成大量的Python函数。