4. 构建C/C++扩展

一个CPython的C扩展是一个共享库(例如一个Linux上的 .so ,或者Windows上的 .pyd ),其会导出一个 初始化函数

To be importable, the shared library must be available on PYTHONPATH, and must be named after the module name, with an appropriate extension. When using setuptools, the correct filename is generated automatically.

初始化函数的声明如下:

PyObject *PyInit_modulename(void)

It returns either a fully initialized module, or a PyModuleDef instance. See Initializing C modules for details.

对于仅有ASCII编码的模块名,函数必须是 PyInit_<modulename> ,将 <modulename> 替换为模块的名字。当使用 Multi-phase initialization 时,允许使用非ASCII编码的模块名。此时初始化函数的名字是 PyInitU_<modulename> ,而 <modulename> 需要用Python的 punycode 编码,连字号需替换为下划线。在Python里:

def initfunc_name(name):
    try:
        suffix = b'_' + name.encode('ascii')
    except UnicodeEncodeError:
        suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
    return b'PyInit' + suffix

可以在一个动态库里导出多个模块,通过定义多个初始化函数。而导入他们需要符号链接或自定义导入器,因为缺省时只有对应了文件名的函数才会被发现。查看 “一个库里的多模块” 章节,在 PEP 489 了解更多细节。

4.1. Building C and C++ Extensions with setuptools

Python 3.12 and newer no longer come with distutils. Please refer to the setuptools documentation at https://setuptools.readthedocs.io/en/latest/setuptools.html to learn more about how build and distribute C/C++ extensions with setuptools.